Introduction
VMware vSphere remains the dominant virtualization platform in enterprise datacenters. Managing hundreds of VMs through the vSphere Client works for small environments, but enterprise operations — provisioning dev environments, enforcing snapshot policies, maintaining golden templates, and migrating workloads — need automation. Ansible's community.vmware collection provides 150+ modules covering every vSphere API operation, from VM lifecycle to distributed switch configuration.
Setup
Install the Collection
ansible-galaxy collection install community.vmware
pip install pyvmomi requests
Connection Variables
# group_vars/all/vmware.yml
vcenter_hostname: vcenter.example.com
vcenter_username: "{{ vault_vcenter_user }}"
vcenter_password: "{{ vault_vcenter_password }}"
vcenter_validate_certs: true
vcenter_datacenter: DC-Primary
vcenter_cluster: Cluster-Prod
Module Reference
| Module | Purpose |
|---|---|
vmware_guest | Create, clone, modify, delete VMs |
vmware_guest_powerstate | Start, stop, restart, suspend VMs |
vmware_guest_snapshot | Create, delete, revert snapshots |
vmware_guest_disk | Add, remove, resize disks |
vmware_guest_network | Manage VM network adapters |
vmware_vm_inventory | Dynamic inventory from vCenter |
vmware_content_deploy_template | Deploy from content library |
vmware_dvswitch | Manage distributed virtual switches |
vmware_host | ESXi host management |
vmware_datastore_info | Datastore capacity reporting |
Pattern 1: VM Provisioning from Template
---
- name: Provision VMs from template
hosts: localhost
connection: local
vars:
vm_specs:
- name: web-prod-01
template: rhel9-golden
folder: /Production/Web
num_cpus: 4
memory_mb: 8192
disk_gb: 100
network: VLAN-100-Production
ip: 10.1.100.11
gateway: 10.1.100.1
- name: web-prod-02
template: rhel9-golden
folder: /Production/Web
num_cpus: 4
memory_mb: 8192
disk_gb: 100
network: VLAN-100-Production
ip: 10.1.100.12
gateway: 10.1.100.1
- name: db-prod-01
template: rhel9-golden
folder: /Production/Database
num_cpus: 8
memory_mb: 32768
disk_gb: 500
network: VLAN-200-Database
ip: 10.1.200.11
gateway: 10.1.200.1
tasks:
- name: Deploy VMs from template
community.vmware.vmware_guest:
hostname: "{{ vcenter_hostname }}"
username: "{{ vcenter_username }}"
password: "{{ vcenter_password }}"
validate_certs: "{{ vcenter_validate_certs }}"
datacenter: "{{ vcenter_datacenter }}"
cluster: "{{ vcenter_cluster }}"
name: "{{ item.name }}"
template: "{{ item.template }}"
folder: "{{ item.folder }}"
state: poweredon
hardware:
num_cpus: "{{ item.num_cpus }}"
memory_mb: "{{ item.memory_mb }}"
disk:
- size_gb: "{{ item.disk_gb }}"
type: thin
datastore: DS-Production-SSD
networks:
- name: "{{ item.network }}"
ip: "{{ item.ip }}"
netmask: 255.255.255.0
gateway: "{{ item.gateway }}"
dns_servers:
- 10.1.1.10
- 10.1.1.11
customization:
hostname: "{{ item.name }}"
domain: example.com
dns_servers:
- 10.1.1.10
- 10.1.1.11
wait_for_customization: true
wait_for_customization_timeout: 600
wait_for_ip_address: true
loop: "{{ vm_specs }}"
register: deployed_vms
- name: Display VM details
ansible.builtin.debug:
msg: "{{ item.item.name }}: {{ item.instance.ipv4 }}"
loop: "{{ deployed_vms.results }}"
loop_control:
label: "{{ item.item.name }}"
Pattern 2: Golden Template Pipeline
---
- name: Build golden VM template
hosts: localhost
connection: local
vars:
template_name: "rhel9-golden-{{ lookup('pipe', 'date +%Y%m%d') }}"
base_vm: rhel9-template-builder
tasks:
- name: Clone base VM for building
community.vmware.vmware_guest:
hostname: "{{ vcenter_hostname }}"
username: "{{ vcenter_username }}"
password: "{{ vcenter_password }}"
validate_certs: "{{ vcenter_validate_certs }}"
datacenter: "{{ vcenter_datacenter }}"
cluster: "{{ vcenter_cluster }}"
name: "{{ base_vm }}"
template: rhel9-base
folder: /Templates/Build
state: poweredon
wait_for_ip_address: true
register: build_vm
- name: Wait for SSH to become available
ansible.builtin.wait_for:
host: "{{ build_vm.instance.ipv4 }}"
port: 22
delay: 30
timeout: 300
delegate_to: localhost
- name: Add build VM to inventory
ansible.builtin.add_host:
name: "{{ build_vm.instance.ipv4 }}"
groups: template_build
ansible_user: root
- name: Configure golden template
hosts: template_build
become: true
tasks:
- name: Update all packages
ansible.builtin.dnf:
name: "*"
state: latest
- name: Install standard packages
ansible.builtin.dnf:
name:
- vim
- tmux
- htop
- chrony
- open-vm-tools
- python3
- rsyslog
state: present
- name: Apply CIS Level 1 baseline
ansible.builtin.include_role:
name: cis-rhel9-baseline
- name: Configure monitoring agent
ansible.builtin.include_role:
name: monitoring-agent
- name: Clean up for templating
ansible.builtin.shell: |
dnf clean all
rm -rf /var/cache/dnf
rm -f /etc/ssh/ssh_host_*
truncate -s 0 /etc/machine-id
rm -f /var/log/*.log
rm -f /root/.bash_history
history -c
- name: Convert to template
hosts: localhost
connection: local
tasks:
- name: Shutdown build VM
community.vmware.vmware_guest_powerstate:
hostname: "{{ vcenter_hostname }}"
username: "{{ vcenter_username }}"
password: "{{ vcenter_password }}"
validate_certs: "{{ vcenter_validate_certs }}"
name: "{{ base_vm }}"
state: shutdown-guest
state_change_timeout: 120
- name: Convert VM to template
community.vmware.vmware_guest:
hostname: "{{ vcenter_hostname }}"
username: "{{ vcenter_username }}"
password: "{{ vcenter_password }}"
validate_certs: "{{ vcenter_validate_certs }}"
name: "{{ base_vm }}"
is_template: true
- name: Rename to dated template name
community.vmware.vmware_guest:
hostname: "{{ vcenter_hostname }}"
username: "{{ vcenter_username }}"
password: "{{ vcenter_password }}"
validate_certs: "{{ vcenter_validate_certs }}"
name: "{{ base_vm }}"
new_name: "{{ template_name }}"
Pattern 3: Snapshot Management
---
- name: Enforce snapshot policy across all VMs
hosts: localhost
connection: local
vars:
max_snapshot_age_days: 7
max_snapshots_per_vm: 3
tasks:
- name: Get all VMs with snapshots
community.vmware.vmware_guest_snapshot_info:
hostname: "{{ vcenter_hostname }}"
username: "{{ vcenter_username }}"
password: "{{ vcenter_password }}"
validate_certs: "{{ vcenter_validate_certs }}"
datacenter: "{{ vcenter_datacenter }}"
folder: "/{{ vcenter_datacenter }}/vm"
register: all_snapshots
- name: Remove snapshots older than policy allows
community.vmware.vmware_guest_snapshot:
hostname: "{{ vcenter_hostname }}"
username: "{{ vcenter_username }}"
password: "{{ vcenter_password }}"
validate_certs: "{{ vcenter_validate_certs }}"
datacenter: "{{ vcenter_datacenter }}"
name: "{{ item.0.guest_name }}"
snapshot_name: "{{ item.1.name }}"
state: absent
loop: "{{ all_snapshots.guest_snapshots | subelements('snapshots', skip_missing=True) }}"
when:
- item.1.creation_time is defined
- (now() - item.1.creation_time | to_datetime).days > max_snapshot_age_days
loop_control:
label: "{{ item.0.guest_name }}: {{ item.1.name }}"
- name: Create pre-maintenance snapshot
community.vmware.vmware_guest_snapshot:
hostname: "{{ vcenter_hostname }}"
username: "{{ vcenter_username }}"
password: "{{ vcenter_password }}"
validate_certs: "{{ vcenter_validate_certs }}"
datacenter: "{{ vcenter_datacenter }}"
name: "{{ item }}"
snapshot_name: "pre-maintenance-{{ ansible_date_time.date }}"
description: "Pre-maintenance snapshot by Ansible"
memory_dump: false
quiesce: true
state: present
loop: "{{ groups['maintenance_targets'] }}"
Pattern 4: Capacity Reporting
---
- name: Generate datacenter capacity report
hosts: localhost
connection: local
tasks:
- name: Get datastore information
community.vmware.vmware_datastore_info:
hostname: "{{ vcenter_hostname }}"
username: "{{ vcenter_username }}"
password: "{{ vcenter_password }}"
validate_certs: "{{ vcenter_validate_certs }}"
datacenter: "{{ vcenter_datacenter }}"
register: datastore_info
- name: Get cluster resource usage
community.vmware.vmware_cluster_info:
hostname: "{{ vcenter_hostname }}"
username: "{{ vcenter_username }}"
password: "{{ vcenter_password }}"
validate_certs: "{{ vcenter_validate_certs }}"
datacenter: "{{ vcenter_datacenter }}"
cluster_name: "{{ vcenter_cluster }}"
register: cluster_info
- name: Display datastore capacity
ansible.builtin.debug:
msg: >
{{ item.name }}:
Total={{ (item.capacity / 1073741824) | round(1) }}GB,
Free={{ (item.freeSpace / 1073741824) | round(1) }}GB,
Used={{ ((item.capacity - item.freeSpace) / item.capacity * 100) | round(1) }}%
loop: "{{ datastore_info.datastores }}"
loop_control:
label: "{{ item.name }}"
- name: Alert on low-capacity datastores
ansible.builtin.mail:
host: smtp.internal
to: vmware-ops@example.com
subject: "⚠️ Datastore {{ item.name }} at {{ ((item.capacity - item.freeSpace) / item.capacity * 100) | round(1) }}% capacity"
body: "Datastore {{ item.name }} has only {{ (item.freeSpace / 1073741824) | round(1) }}GB free."
loop: "{{ datastore_info.datastores }}"
when: (item.freeSpace / item.capacity) < 0.15
loop_control:
label: "{{ item.name }}"
Pattern 5: Dynamic Inventory from vCenter
# inventory/vmware.yml
plugin: community.vmware.vmware_vm_inventory
hostname: vcenter.example.com
username: "ansible-svc@vsphere.local"
password: "{{ lookup('env', 'VMWARE_PASSWORD') }}"
validate_certs: true
with_nested_properties: true
hostnames:
- config.name
keyed_groups:
- key: config.guestId
prefix: os
- key: guest.net[0].network
prefix: network
- key: "tags.get('environment', 'unknown')"
prefix: env
filters:
- runtime.powerState == "poweredOn"
- config.template == false
compose:
ansible_host: guest.ipAddress
ansible_user: "'ansible'"
# Test the dynamic inventory
ansible-inventory -i inventory/vmware.yml --graph
Troubleshooting
SSL Certificate Errors
# Use a CA bundle or disable verification (dev only!)
vcenter_validate_certs: true
# Export the vCenter CA cert:
# openssl s_client -connect vcenter.example.com:443 < /dev/null 2>/dev/null | openssl x509 > vcenter-ca.pem
# Then set: REQUESTS_CA_BUNDLE=/path/to/vcenter-ca.pem
VM Customization Timeout
# Increase timeout for slow guest customization
wait_for_customization: true
wait_for_customization_timeout: 900 # 15 minutes
Permission Errors
Ansible needs a vCenter service account with these minimum privileges:
- Virtual Machine: All privileges
- Datastore: Allocate space, Browse
- Network: Assign network
- Resource: Assign VM to resource pool
- Content Library: Download files (if using templates)
Related Articles
- Create VMware VM
- Deploy VM from Template
- Start/Stop VMware VMs
- VMware Datastore Tags
- Configure Ansible for VMware
Conclusion
Ansible's community.vmware collection covers the full VM lifecycle: provision from templates with vmware_guest, enforce snapshot policies, build golden templates automatically, generate capacity reports, and use vCenter as a dynamic inventory source. For enterprise environments, combine with AAP for self-service VM provisioning (surveys for CPU/memory/network), scheduled snapshot cleanup, and monthly template rebuilds. The key is treating VMs like cattle, not pets — automate creation, configuration, and decommissioning end to end.