VMware vSphere is the dominant on-premises hypervisor — a massive footprint at banks, telcos, governments, and any enterprise older than five years. Most vSphere environments grow organically: someone right-clicks a host in the GUI, then a colleague does the same for ten more, and within two years the click-ops debt is impossible to audit. Ansible breaks that cycle. The community.vmware collection wraps vCenter’s API and lets you manage thousands of VMs, hundreds of clusters, datastores, networks, and resource pools with the same playbook discipline you apply to Linux servers — and the same pattern of source-controlled, peer-reviewable, auditable change.
This lesson covers the community.vmware collection in EX374-grade depth: how Ansible authenticates to vCenter, the most-used modules for VM lifecycle (provisioning from templates, cloning, customization), datacenter/cluster/host config, distributed virtual switches and port groups, datastore management, snapshots, NSX-T network automation (when you need software-defined networking on top), and the pattern of inventory-from-vCenter so your live VM inventory is always the source of truth.
Learning Objectives
By the end you will be able to:
- Install
community.vmwareand thepyvmomiclient library on the control node. - Authenticate to vCenter using username/password or OAuth tokens, with hostname validation.
- Use
vmware_guestfor full VM lifecycle (create, clone, customize, power on/off, delete, snapshot). - Provision VMs from templates with cloud-init or VMware Customization Specs.
- Use
vmware_host,vmware_cluster,vmware_datacenter,vmware_resource_poolfor vSphere object lifecycle. - Manage networks with
vmware_dvswitch,vmware_dvs_portgroup,vmware_portgroup(vSwitch). - Manage datastores with
vmware_datastore_*and storage policies withvmware_storage_policy. - Use
community.vmware.vmware_vm_inventoryas a dynamic inventory source. - Configure NSX-T with the
vmware.ansible_for_nsxtcollection (logical routers, segments, firewalls). - Apply patterns for managing 500+ VM environments: tags, folders, custom attributes, role-based scoping.
Prerequisites
- Tier 1–3 Ansible fluency.
- Working knowledge of vSphere concepts: vCenter, ESXi, datacenter, cluster, host, datastore, vSwitch, dvSwitch, portgroup, VM templates.
- A vSphere lab — VMware offers a free vSphere Hypervisor (ESXi) for non-production use, plus a 60-day vCenter eval. The “VMware Hands-on Labs” portal is also free.
- Or: a free Nutanix CE / Proxmox lab if you don’t have access to vSphere — most concepts translate.
Mental Model: Ansible Talks to vCenter, Not ESXi
1. vCenter is the API; ESXi is the worker
Every community.vmware module calls vCenter (port 443, HTTPS, SOAP+REST). vCenter then dispatches operations to the appropriate ESXi host. You almost never talk to ESXi directly from Ansible — vCenter is the API surface. The exception: standalone ESXi hosts without vCenter. Then hostname: points at ESXi directly and most modules still work, but features like vMotion, DRS, HA aren’t available.
2. pyvmomi is the Python SDK underneath
community.vmware uses pyvmomi, VMware’s official Python client. Install it on the control node — the modules import it at runtime. Some newer modules use the vSphere REST SDK (vmware-vapi-runtime); install that too for full module coverage.
3. vCenter objects have an inventory hierarchy: vCenter > Datacenter > Cluster > Host > VM
Every module needs to know where in the hierarchy to act. Most modules accept datacenter, cluster, folder, resource_pool, name parameters and the module figures out the path. Naming conflicts (two VMs called web-01 in different folders) are common — always specify folder: or use the unique uuid: parameter.
4. VM lifecycle from templates is the canonical pattern
Click-ops VM creation: build OS, install patches, install agents, create AD account. Ansible-driven: build a template once (golden image), then vmware_guest: state=poweredon, template=tpl-rhel9-base, customization={...} per VM. Use linked_clone: true for fast provisioning of dev/test fleets, or template: tpl-rhel9-base (full clone) for production VMs.
5. NSX-T is a separate collection
VMware’s software-defined networking (NSX-T) has its own collection: vmware.ansible_for_nsxt. The modules look like the vSphere ones but talk to the NSX Manager API, not vCenter. If your environment doesn’t run NSX, you can skip the NSX section — vSphere networking via dvSwitch is sufficient for many use cases.
Setting Up the Control Node
# Python deps
python3 -m pip install --user 'pyvmomi>=8.0.2' 'requests>=2.31'
# vSphere REST SDK (newer modules need this)
python3 -m pip install --user 'vmware-vapi-runtime' 'vmware-vapi-common-client' \
'vmware-vcenter-bindings'
# Collections
ansible-galaxy collection install community.vmware vmware.ansible_for_nsxt
Verify:
python3 -c "from pyVmomi import vim; print('pyvmomi OK')"
ansible-galaxy collection list community.vmware
Authentication and Common Variables
Every community.vmware module needs four parameters: hostname, username, password, validate_certs. Stash them in group_vars so each task isn’t bloated:
# group_vars/vsphere.yml
vcenter_hostname: vcsa.corp.example.com
vcenter_username: "automation@vsphere.local"
vcenter_password: "{{ vault_vcenter_password }}"
vcenter_validate_certs: true # always true in production
Then use the dictionary expansion:
- name: Get vCenter version
community.vmware.vmware_about_info:
hostname: "{{ vcenter_hostname }}"
username: "{{ vcenter_username }}"
password: "{{ vcenter_password }}"
validate_certs: "{{ vcenter_validate_certs }}"
register: vc_info
Production pattern: store credentials in HashiCorp Vault or AAP credential store, not in Vault-encrypted YAML files. The vCenter automation user should be a dedicated SSO account with minimum-needed RBAC (typically a custom role with VM provisioning + datastore access, not full admin).
The community.vmware Collection — Module-by-Module
The collection ships ~150 modules. The most-used:
VM lifecycle
| Module | Purpose |
|---|---|
vmware_guest |
Create/clone/configure/power/delete VMs (the workhorse) |
vmware_guest_powerstate |
Just power state changes (on/off/restart) |
vmware_guest_snapshot |
Snapshot lifecycle |
vmware_guest_disk |
Add/remove/resize VM disks |
vmware_guest_network |
Add/remove/modify vNICs |
vmware_guest_customization_info |
Read OS customization status |
vmware_guest_tools_wait |
Wait for VMware Tools to start |
vmware_guest_info |
Read VM metadata |
vmware_vm_shell |
Run a command inside the guest OS via Tools |
Datacenter / cluster / host
| Module | Purpose |
|---|---|
vmware_datacenter |
Manage datacenters |
vmware_cluster |
Manage clusters with HA/DRS settings |
vmware_host |
Add/remove ESXi hosts to/from clusters |
vmware_host_config_manager |
Bulk apply host advanced settings |
vmware_host_ntp |
NTP config on hosts |
vmware_host_dns_info |
DNS config |
vmware_host_lockdown |
Lockdown mode |
vmware_resource_pool |
Resource pool lifecycle |
Networking (vSwitch / dvSwitch)
| Module | Purpose |
|---|---|
vmware_vswitch |
Manage standard vSwitches on a host |
vmware_portgroup |
Manage standard portgroups |
vmware_dvswitch |
Manage distributed switches |
vmware_dvs_portgroup |
Manage distributed portgroups |
vmware_dvs_host |
Add/remove hosts to dvSwitch |
vmware_vmkernel |
Manage VMkernel adapters (vMotion, mgmt, vSAN) |
Storage
| Module | Purpose |
|---|---|
vmware_datastore_info |
Read datastore state |
vmware_datastore_cluster |
Manage datastore clusters (SDRS) |
vmware_host_datastore |
Mount NFS / iSCSI datastores |
vmware_storage_policy |
Manage SPBM policies |
Inventory and tags
| Module / Plugin | Purpose |
|---|---|
vmware_vm_inventory (inventory plugin) |
Pull VM inventory from vCenter |
vmware_tag |
Manage tag categories and tags |
vmware_tag_manager |
Apply/remove tags to/from objects |
vmware_folder_info |
Read folder hierarchy |
Provisioning a VM from a Template
The canonical pattern. Assumes you’ve built a tpl-rhel9-base template VM (RHEL 9 + basic packages + cloud-init or sysprep).
- hosts: localhost
gather_facts: false
vars:
vcenter_hostname: vcsa.corp.example.com
vcenter_username: "{{ vault_vcenter_username }}"
vcenter_password: "{{ vault_vcenter_password }}"
tasks:
- name: Provision a Linux VM from template
community.vmware.vmware_guest:
hostname: "{{ vcenter_hostname }}"
username: "{{ vcenter_username }}"
password: "{{ vcenter_password }}"
validate_certs: true
name: "{{ vm_name }}"
template: "tpl-rhel9-base"
datacenter: "Primary-DC"
folder: "/Primary-DC/vm/Workloads/Production"
cluster: "Production-Cluster-A"
datastore: "vsanDatastore"
state: poweredon
wait_for_ip_address: true
hardware:
memory_mb: 8192
num_cpus: 4
num_cpu_cores_per_socket: 2
hotadd_cpu: true
hotadd_memory: true
disk:
- size_gb: 60
type: thin
datastore: "vsanDatastore"
networks:
- name: "PG-Production-VLAN-100"
ip: "10.10.100.50"
netmask: "255.255.255.0"
gateway: "10.10.100.1"
dns_servers: ["10.10.0.10", "10.10.0.11"]
customization:
hostname: "{{ vm_name }}"
domain: "corp.example.com"
dns_servers: ["10.10.0.10", "10.10.0.11"]
timezone: "America/Chicago"
annotation: "Provisioned by Ansible {{ ansible_date_time.iso8601 }}"
custom_attributes:
- name: Owner
value: "platform-team"
- name: Environment
value: "production"
register: vm_result
This single task creates the VM, applies customization (hostname/network), powers it on, and waits for the IP. The wait_for_ip_address: true blocks until VMware Tools reports the IP — typically 30–90 seconds for a Linux template.
For Windows VMs, the customization specs are richer (sysprep-driven):
customization:
hostname: "{{ vm_name }}"
domain: "corp.example.com"
joindomain: "corp.example.com"
domainadmin: "Administrator@corp.example.com"
domainadminpassword: "{{ vault_domain_admin_password }}"
password: "{{ vault_local_admin_password }}"
fullname: "Administrator"
orgname: "Example Corp"
productid: "{{ vault_windows_product_key }}"
timezone: 35 # Central Standard Time
autologon: false
runonce:
- "powershell.exe Set-ExecutionPolicy RemoteSigned -Force"
Note timezone: for Windows is the numeric Microsoft TZ ID, not a string.
VM Bulk Provisioning Pattern
Provision 50 VMs from a CSV or YAML list:
# group_vars/staging_fleet.yml
fleet_vms:
- { name: app-stg-01, ip: 10.10.20.11, role: app }
- { name: app-stg-02, ip: 10.10.20.12, role: app }
- { name: app-stg-03, ip: 10.10.20.13, role: app }
- { name: db-stg-01, ip: 10.10.20.21, role: db, memory: 16384, disk: 200 }
# ... 46 more
- name: Provision the staging fleet
community.vmware.vmware_guest:
hostname: "{{ vcenter_hostname }}"
username: "{{ vcenter_username }}"
password: "{{ vcenter_password }}"
name: "{{ item.name }}"
template: "tpl-rhel9-base"
folder: "/Primary-DC/vm/Workloads/Staging"
cluster: "Staging-Cluster"
datastore: "vsanDatastore-Stg"
state: poweredon
wait_for_ip_address: false # don't block the loop
hardware:
memory_mb: "{{ item.memory | default(4096) }}"
num_cpus: "{{ item.cpus | default(2) }}"
disk:
- size_gb: "{{ item.disk | default(40) }}"
type: thin
networks:
- name: "PG-Staging-VLAN-200"
ip: "{{ item.ip }}"
netmask: "255.255.255.0"
gateway: "10.10.20.1"
customization:
hostname: "{{ item.name }}"
domain: "corp.example.com"
loop: "{{ fleet_vms }}"
loop_control:
label: "{{ item.name }}"
async: 600
poll: 0
register: vm_jobs
- name: Wait for all VMs to finish provisioning
ansible.builtin.async_status:
jid: "{{ async_result_item.ansible_job_id }}"
loop: "{{ vm_jobs.results }}"
loop_control:
loop_var: async_result_item
retries: 60
delay: 10
until: async_status_check.finished
register: async_status_check
async: 600 poll: 0 fires each VM creation in parallel (one fork per VM), then the async_status block waits for them all. This pattern provisions 50 VMs in 5–10 minutes versus 50× single provisioning at 30s each = 25 minutes serial.
Snapshots Before a Risky Change
A snapshot is the cheapest insurance for a VM that’s about to be modified:
- name: Take pre-change snapshot
community.vmware.vmware_guest_snapshot:
hostname: "{{ vcenter_hostname }}"
username: "{{ vcenter_username }}"
password: "{{ vcenter_password }}"
name: "{{ inventory_hostname_short }}"
datacenter: "Primary-DC"
folder: "/Primary-DC/vm/Workloads/Production"
state: present
snapshot_name: "pre-os-upgrade-{{ ansible_date_time.epoch }}"
description: "Pre-upgrade snapshot before RHEL 8→9 in-place upgrade"
quiesce: true # quiesce the guest filesystem (requires VMware Tools)
memory_dump: false # don't include memory; faster snapshot
# ... do the risky thing ...
- name: Remove snapshot after success
community.vmware.vmware_guest_snapshot:
hostname: "{{ vcenter_hostname }}"
username: "{{ vcenter_username }}"
password: "{{ vcenter_password }}"
name: "{{ inventory_hostname_short }}"
datacenter: "Primary-DC"
folder: "/Primary-DC/vm/Workloads/Production"
state: absent
snapshot_name: "pre-os-upgrade-{{ ansible_date_time.epoch }}"
Don’t keep snapshots forever — they consume datastore space and slow down VM I/O. Take them before a change, validate, delete on success.
Distributed Virtual Switch and Portgroup Setup
Build the network plumbing for a new datacenter:
- name: Create distributed virtual switch
community.vmware.vmware_dvswitch:
hostname: "{{ vcenter_hostname }}"
username: "{{ vcenter_username }}"
password: "{{ vcenter_password }}"
datacenter: "Primary-DC"
switch: "DVS-Production"
version: 8.0.0
mtu: 9000
uplink_quantity: 4
discovery_protocol: lldp
discovery_operation: both
state: present
- name: Create production VLAN portgroups
community.vmware.vmware_dvs_portgroup:
hostname: "{{ vcenter_hostname }}"
username: "{{ vcenter_username }}"
password: "{{ vcenter_password }}"
switch_name: "DVS-Production"
portgroup_name: "{{ item.name }}"
vlan_id: "{{ item.vlan }}"
num_ports: "{{ item.ports | default(120) }}"
portgroup_type: earlyBinding
network_policy:
promiscuous: false
forged_transmits: false
mac_changes: false
state: present
loop:
- { name: "PG-Production-VLAN-100", vlan: 100 }
- { name: "PG-Production-VLAN-101", vlan: 101 }
- { name: "PG-vMotion-VLAN-200", vlan: 200 }
- { name: "PG-iSCSI-VLAN-300", vlan: 300 }
Then add hosts to the dvSwitch:
- name: Add ESXi hosts to dvSwitch
community.vmware.vmware_dvs_host:
hostname: "{{ vcenter_hostname }}"
username: "{{ vcenter_username }}"
password: "{{ vcenter_password }}"
esxi_hostname: "{{ item }}"
switch_name: "DVS-Production"
vmnics:
- vmnic2
- vmnic3
state: present
loop: "{{ groups['esxi_hosts'] }}"
Cluster Configuration with HA and DRS
- name: Create cluster with HA and DRS
community.vmware.vmware_cluster:
hostname: "{{ vcenter_hostname }}"
username: "{{ vcenter_username }}"
password: "{{ vcenter_password }}"
datacenter_name: "Primary-DC"
cluster_name: "Production-Cluster-A"
state: present
- name: Configure HA on cluster
community.vmware.vmware_cluster_ha:
hostname: "{{ vcenter_hostname }}"
username: "{{ vcenter_username }}"
password: "{{ vcenter_password }}"
datacenter_name: "Primary-DC"
cluster_name: "Production-Cluster-A"
enable: true
ha_host_monitoring: enabled
ha_admission_control_enabled: true
slot_based_admission_control:
cpu_failover_resources_percent: 50
memory_failover_resources_percent: 50
ha_vm_monitoring: vmAndAppMonitoring
ha_restart_priority: medium
apd_response: restartConservative
pdl_response: restartAggressive
- name: Configure DRS on cluster
community.vmware.vmware_cluster_drs:
hostname: "{{ vcenter_hostname }}"
username: "{{ vcenter_username }}"
password: "{{ vcenter_password }}"
datacenter_name: "Primary-DC"
cluster_name: "Production-Cluster-A"
enable: true
drs_default_vm_behavior: fullyAutomated
drs_vmotion_rate: 3 # 1 (conservative) – 5 (aggressive)
drs_enable_vm_behavior_overrides: true
Dynamic Inventory from vCenter
The community.vmware.vmware_vm_inventory plugin pulls live VM inventory from vCenter — perfect for “run a play on every prod VM.”
# inventory/vsphere.yml
plugin: community.vmware.vmware_vm_inventory
strict: false
hostname: vcsa.corp.example.com
username: "{{ lookup('env', 'VCENTER_USERNAME') }}"
password: "{{ lookup('env', 'VCENTER_PASSWORD') }}"
validate_certs: true
with_tags: true
hostnames:
- 'config.name'
properties:
- 'config.name'
- 'guest.ipAddress'
- 'config.guestId'
- 'runtime.powerState'
- 'config.annotation'
- 'tag'
filters:
- "runtime.powerState == 'poweredOn'"
- "'production' in tag"
compose:
ansible_host: guest.ipAddress
ansible_python_interpreter: '"/usr/bin/python3"'
keyed_groups:
- key: tag
prefix: tag
- key: config.guestId
prefix: os
Then run:
ansible-inventory -i inventory/vsphere.yml --graph
ansible -i inventory/vsphere.yml tag_production -m ping
The plugin queries vCenter and produces an inventory with groups like tag_production, tag_finance, os_rhel9_64Guest. Live inventory beats static YAML files for any environment with > 50 VMs.
NSX-T Network Automation (Optional)
If your environment uses NSX-T, the vmware.ansible_for_nsxt collection adds modules for software-defined networking:
- name: Create NSX-T overlay segment
vmware.ansible_for_nsxt.nsxt_policy_segment:
hostname: "{{ nsxt_manager }}"
username: "{{ nsxt_username }}"
password: "{{ nsxt_password }}"
validate_certs: true
state: present
display_name: "seg-app-tier"
transport_zone_display_name: "TZ-Overlay"
subnets:
- gateway_address: "10.50.1.1/24"
advanced_config:
connectivity: "ON"
- name: Apply distributed firewall rule
vmware.ansible_for_nsxt.nsxt_policy_security_policy:
hostname: "{{ nsxt_manager }}"
username: "{{ nsxt_username }}"
password: "{{ nsxt_password }}"
validate_certs: true
state: present
display_name: "app-tier-policy"
category: "Application"
rules:
- display_name: "allow-web-to-app"
source_groups: ["/infra/domains/default/groups/web-tier"]
destination_groups: ["/infra/domains/default/groups/app-tier"]
services: ["/infra/services/HTTP"]
action: ALLOW
NSX automation is a deep topic on its own — the collection has 50+ modules covering Tier-0/Tier-1 routers, NAT rules, load balancers, IPAM. The pattern is the same as community.vmware: REST API wrapped in idempotent modules.
Hands-on Free Lab: Provision Three VMs from a Template
Free with VMware Hands-on Labs (HOL-2401-01) or a 60-day vCenter eval. The playbook below assumes a working template called tpl-photon-base (Photon OS template that ships with HOL).
# inventory.yml
all:
hosts:
localhost:
ansible_connection: local
# group_vars/all.yml
vcenter_hostname: vcsa-01a.corp.local
vcenter_username: administrator@vsphere.local
vcenter_password: VMware1!
vcenter_validate_certs: false # HOL uses self-signed certs
vm_list:
- { name: ansible-vm-01, ip: 192.168.110.101 }
- { name: ansible-vm-02, ip: 192.168.110.102 }
- { name: ansible-vm-03, ip: 192.168.110.103 }
# site.yml
- hosts: localhost
gather_facts: false
tasks:
- name: Provision lab VMs
community.vmware.vmware_guest:
hostname: "{{ vcenter_hostname }}"
username: "{{ vcenter_username }}"
password: "{{ vcenter_password }}"
validate_certs: "{{ vcenter_validate_certs }}"
name: "{{ item.name }}"
template: "tpl-photon-base"
datacenter: "RegionA01"
folder: "/RegionA01/vm/Discovered virtual machine"
cluster: "RegionA01-COMP01"
datastore: "RegionA01-ISCSI01-COMP01"
state: poweredon
wait_for_ip_address: true
hardware:
memory_mb: 1024
num_cpus: 1
networks:
- name: "VM-RegionA01-vDS-COMP"
ip: "{{ item.ip }}"
netmask: "255.255.255.0"
gateway: "192.168.110.1"
customization:
hostname: "{{ item.name }}"
domain: "corp.local"
loop: "{{ vm_list }}"
loop_control:
label: "{{ item.name }}"
- name: Tag VMs as Ansible-managed
community.vmware.vmware_tag_manager:
hostname: "{{ vcenter_hostname }}"
username: "{{ vcenter_username }}"
password: "{{ vcenter_password }}"
validate_certs: "{{ vcenter_validate_certs }}"
tag_names:
- "Ansible-Managed"
object_name: "{{ item.name }}"
object_type: VirtualMachine
state: present
loop: "{{ vm_list }}"
loop_control:
label: "{{ item.name }}"
Run: ansible-playbook -i inventory.yml site.yml. Three VMs come up in 2–3 minutes. Tear down: rerun with state: absent.
Common Mistakes & Troubleshooting
1. “VM not found” on a clone task
You specified the template by name without folder: — vCenter found two templates with the same name in different folders. Always specify folder: to disambiguate.
2. Customization fails silently — VM comes up with template’s hostname
VMware Tools isn’t installed, or the template’s OS isn’t supported by the customization spec. Check vmware_guest_customization_info after provisioning.
3. community.vmware.vmware_guest reports changed: true every run
Annotation field is being mangled (newlines, encoding). Or custom_attributes includes a key that’s not defined in vCenter. Run with -vv to see the diff.
4. SSL certificate validation fails against vCenter with a real cert
The cert chain is incomplete, or the control node’s CA bundle is out of date. Either install the full chain on the control node, or temporarily set validate_certs: false (production: never).
5. wait_for_ip_address: true hangs forever
VMware Tools isn’t running or isn’t reporting an IP yet. Set a timeout: wait_for_ip_address_timeout: 300. If your template doesn’t have Tools, use wait_for_customization: true instead, which polls the customization status.
6. Bulk provisioning hits vCenter rate limits
vCenter throttles concurrent operations. Set forks: 5 (not 50), or use serial: 5 in the play. Production vCenter handles 5–10 concurrent provisioning tasks comfortably; more than that needs vCenter HA.
7. NSX-T module fails with “Invalid manager IP”
NSX collection talks to NSX Manager (not vCenter). The hostnames are different. Don’t confuse nsxt_manager (e.g. nsxmgr.corp.local) with vcenter_hostname.
Best Practices
- Always specify
folder:when working with VMs. Names aren’t unique across folders;folder:disambiguates. - Tag every Ansible-provisioned VM. A
vmware_tag_managertask at the end of every provisioning play. The dynamic inventory plugin filters by tag. - Snapshot before risky changes — and delete after success. Snapshots are cheap insurance, not durable backups.
- Use
async/poll: 0for bulk operations. Serial provisioning of 50 VMs takes 50× the time of parallel. - Pin collection versions.
community.vmware: 5.5.0inrequirements.yml. New versions occasionally rename parameters. - Use
vmware_vm_inventoryfor “operate on existing VMs” plays. Don’t maintain a static YAML inventory of 1000 VMs by hand. - Separate provisioning plays from configuration plays. Provisioning runs against vCenter (control node only). Configuration runs against the VMs (SSH/WinRM). Don’t mix.
- Keep a “golden template” for each OS.
tpl-rhel9-base,tpl-windows2022-base. Update templates monthly with patches; reprovision over upgrade. - Treat ESXi as cattle. Don’t customize hosts manually; build them via Auto Deploy + Host Profiles or ESXi kickstart, then
vmware_host_config_managerfor ongoing config.
Security Notes
- Use a dedicated vCenter SSO user for Ansible automation. Custom role with minimum-needed privileges, audit trail in vCenter.
- Validate certs in production.
validate_certs: falseis a development-only setting. In production, the control node must trust the vCenter CA. - Encrypt vCenter credentials with Ansible Vault. No
password: vmware1!in plaintext. - Audit vCenter events. Every Ansible action becomes a vCenter Event entry. Forward those to your SIEM.
- Lock down NSX-T manager. Same as vCenter — dedicated user, audit trail, vault-encrypted creds.
- Don’t reuse the same automation user across environments. Separate
automation-prod,automation-stg,automation-devSSO users with environment-scoped permissions.
Q&A — 13 Questions
Q1. Can I run Ansible against a free ESXi (no vCenter)?
Yes — hostname: esxi-host.corp.local, but features like vMotion, DRS, HA, and most cluster modules don’t work without vCenter. VM lifecycle, networking, and host config still work.
Q2. What’s the difference between linked_clone and full clone?
A linked clone shares disk blocks with the template — fast to provision, but you can’t delete the template without breaking the clones. Use linked for ephemeral dev/test, full for production.
Q3. Why does vmware_guest need a template parameter for customization?
The customization spec (cloud-init / sysprep) is template-aware. vCenter can’t fully customize a VM that wasn’t created from a template — the OS-customization-on-first-boot machinery requires the template metadata.
Q4. Can I use Terraform’s vSphere provider instead? Yes — and many shops do. Terraform handles the infrastructure (VMs, networks, datastores) declaratively, then Ansible handles configuration (apt install, render configs, start services). The two complement each other; pick one for VM creation.
Q5. How do I pass cloud-init user-data to a VMware VM?
vmware_guest doesn’t directly accept cloud-init. Instead, use the customization block (which uses VMware’s Customization Specs), or attach an ISO with the cloud-init user-data and meta-data files via vmware_guest_disk (advanced — most teams stick with Customization Specs).
Q6. What does quiesce: true do on a snapshot?
It tells VMware Tools to flush filesystem buffers and pause writes during the snapshot. Required for crash-consistent snapshots; on by default for VSS-aware Windows guests, optional for Linux.
Q7. Can I migrate a VM with vmware_guest?
Use vmware_vmotion instead — it’s the dedicated module for vMotion (compute), Storage vMotion (disk), or both.
Q8. How do I shrink a VM disk? You can’t via Ansible (or vSphere directly) — VM disk shrinking requires guest-OS coordination. The pattern is: shrink the filesystem inside the guest, then the vSphere “compact” operation reclaims the freed blocks at the datastore level.
Q9. What’s vmware_resource_pool for?
Resource pools are RBAC and resource-allocation containers within a cluster. You assign VMs to pools, set CPU/memory shares, and grant permissions at the pool level. Useful for multi-tenant clusters.
Q10. How do I deploy a VM with multiple disks?
disk: is a list — provide as many entries as you need. Each disk gets a distinct unit_number (the SCSI ID).
Q11. Why does vmware_vm_inventory return inventory but Ansible can’t connect to the VMs?
The plugin returns the management IP, but the VM might not be reachable from the control node (network ACL, firewall, VPN). Test with ansible -m ping against one host and debug from there.
Q12. What’s the right way to manage ESXi host advanced settings?
community.vmware.vmware_host_config_manager with options: set to a dict of Setting.Path: value pairs. Bulk-apply across a cluster with a loop.
Q13. How do I integrate vSphere Tags with workflow tools like ServiceNow?
Provision the VM with vmware_tag_manager setting tags like ServiceNow-CI: CI0001234. Then vmware_vm_inventory filters/groups by tag, and your config plays can reach back into ServiceNow via the community.general.snow_record module.
Quick Check
- Which Python library does
community.vmwaredepend on? - What’s the canonical pattern for provisioning a VM?
- What does
wait_for_ip_address: truedo? - Which module manages VM snapshots?
- What’s the difference between
vmware_dvswitchandvmware_vswitch? - Which inventory plugin pulls live inventory from vCenter?
- What does
quiesce: truemean on a snapshot? - Which collection ships NSX-T modules?
Exercise
Build a complete role vsphere_app_stack that:
- Provisions 3 application VMs and 1 database VM from
tpl-rhel9-base. - Places app VMs in a “App-Tier” resource pool, DB VM in “DB-Tier”.
- Creates a dvSwitch portgroup
PG-App-Tier(VLAN 200) if missing. - Tags every VM with
Ansible-Managed,App: myapp, andEnvironment: prod. - Snapshots all VMs before applying changes.
- Configures cluster HA settings (admission control, restart priority).
- Exports a dynamic inventory grouping by tags.
- Includes a
validate.ymltask list that confirms VMware Tools is running on every VM.
Test the role by running it twice — second run should report all ok, no changed. Verify with the dynamic inventory plugin that the VMs are correctly tagged and grouped.
Cert Mapping
- EX374 — VMware automation is one of the seven domain blocks. Expect a hands-on task to provision VMs, configure networking, or apply cluster settings.
- VCP-DCV (VMware Certified Professional) — vSphere knowledge directly transfers. Ansible automation is a +bonus for the cert; not directly tested but extremely useful in real work.
- VCAP-DCV-Deploy — Programmatic vSphere management is core; Ansible is an explicitly mentioned tool.
Glossary
- vCenter — VMware’s management plane; SOAP+REST API at port 443.
- ESXi — VMware’s bare-metal hypervisor; vCenter manages many ESXi hosts.
- DRS — Distributed Resource Scheduler. Auto-balances VMs across hosts in a cluster.
- HA — High Availability. Restarts VMs on a different host when their host fails.
- dvSwitch — Distributed virtual switch; spans multiple ESXi hosts.
- VMkernel — A management network interface on ESXi (used for management, vMotion, vSAN).
- NSX-T — VMware’s software-defined networking layer; separate from vCenter.
- pyvmomi — VMware’s official Python SDK for vSphere API.
- Customization Spec — vCenter’s stored OS customization template (sysprep for Windows, cloud-init-like for Linux).
- vSAN — VMware’s hyperconverged storage; pools local SSDs across ESXi hosts.
Next Steps
You can now drive vSphere from Ansible at scale — VMs, networks, datacenters, NSX-T — and integrate vCenter as a source of dynamic inventory. The next and final lesson in this expert tier covers hybrid and multi-cloud orchestration: combining on-prem (vSphere/network), public cloud (AWS/Azure/GCP), and Kubernetes into a single Ansible-driven workflow with AAP, automation mesh, and the orchestration patterns that big enterprises use to coordinate change across all of it.