In a nutshell
Imagine a giant apartment building. Every unit is a virtual machine, each floor is an ESXi host, and there is one building manager sitting at the front desk: vCenter. When you want fifty identical furnished units, you do not walk each floor hanging pictures and making beds — you hand the building manager a single work order: “make fifty copies of the model apartment (a template), put these on floor 3, give them these mailbox numbers.” The manager dispatches the actual work to the right floor superintendent (the ESXi host) and reports back when each unit is ready.
Ansible is the tool that writes those work orders — in YAML, kept in Git, reviewed by a colleague — instead of you clicking through a GUI a thousand times. The community.vmware collection is the stack of pre-printed order forms: one module to create a VM, one to snapshot it, one to build a network, one to configure a cluster. You fill in the blanks (which template, which datastore, how much RAM) and Ansible hands the completed form to vCenter’s API.
The single most important thing to internalize: Ansible talks to vCenter, not to the VMs and not to ESXi. During provisioning your playbook runs on localhost (the control node) and makes HTTPS API calls to vCenter. The VMs you are creating are not yet in your SSH inventory — they do not even exist yet. Configuring the inside of those VMs (installing packages, editing configs, starting services) is a completely separate phase that runs later, over SSH or WinRM. Keep those two phases apart in your head and most VMware-automation confusion disappears.
Level: Advanced · Time: ~50 min
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.
- Choose between
community.vmware(pyvmomi) andvmware.vmware_rest(REST/aiohttp), and know when each fits. - Recognize and work around
vmware_guestidempotency limits (spuriouschanged, customization-only-at-create).
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.
- Helpful siblings: Vault & secrets (for vCenter credentials), Inventory fundamentals (dynamic-inventory concepts), and Performance tuning (forks,
async) all pay off here.
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 |
Note on names: the tables above use the short module names for readability. In real playbooks always write the fully-qualified collection name (FQCN) —
community.vmware.vmware_guest, notvmware_guest. FQCNs are unambiguous, survive multiple collections defining similar names, and are what EX374 expects.
Which Collection? community.vmware vs vmware.vmware_rest vs vmware.vmware
Everything in this lesson uses community.vmware, and for most shops that is still the right default. But as of vSphere 8 (the Broadcom era) there are three collections in play, and EX374-grade work means knowing which is which — and why a task might fail on one but not another.
community.vmware — the veteran. Community-maintained, wraps the pyvmomi SOAP SDK, ~150 modules, the widest coverage by far (hosts, clusters, dvSwitch, SPBM, vSAN, content library, guest operations). Connection params are hostname / username / password / validate_certs. This is what the whole lesson above uses.
vmware.vmware_rest — Red Hat-certified, auto-generated from vCenter’s REST API bindings, built on the async aiohttp library (not pyvmomi). Modules are named vcenter_*, appliance_*, content_*. Connection params are prefixed: vcenter_hostname / vcenter_username / vcenter_password / vcenter_validate_certs (and it honours the VMWARE_HOST / VMWARE_USER / VMWARE_PASSWORD / VMWARE_VALIDATE_CERTS environment variables). It needs vSphere 7.0.2 or later and is more low-level: to create a VM you first resolve the MoIDs (managed object IDs) of the cluster, datastore and folder with *_info modules, then feed those IDs into vcenter_vm. More verbose — but it is the officially supported REST path and gets new-feature coverage (appliance management, content library) that community modules sometimes lag on.
vmware.vmware — the newest, curated collection that has begun consolidating supported content under the Broadcom/Red Hat umbrella. Treat it as the strategic direction rather than today’s daily driver: check its module list before assuming a given task exists there, and keep community.vmware for the long tail.
community.vmware |
vmware.vmware_rest |
|
|---|---|---|
| Underlying SDK | pyvmomi (SOAP) | aiohttp (REST) |
| Python dependency | pyvmomi, requests |
aiohttp |
| Min vSphere | 6.5+ (varies by module) | 7.0.2+ |
| Connection params | hostname, username, password, validate_certs |
vcenter_hostname, vcenter_username, vcenter_password, vcenter_validate_certs |
| VM-create module | vmware_guest (name-based) |
vcenter_vm (MoID-based) |
| Coverage | Very broad (~150 modules) | REST-API surface; growing |
| Ergonomics | High-level, name-based | Lower-level, resolve IDs first |
| Best for | Day-to-day fleet automation | REST-native shops, appliance/content-library tasks, newest features |
The same VM, created through the REST collection, looks like this — note the two-step “resolve the ID, then use it” rhythm that defines vmware.vmware_rest:
- hosts: localhost
gather_facts: false
environment:
VMWARE_HOST: "{{ vcenter_hostname }}"
VMWARE_USER: "{{ vcenter_username }}"
VMWARE_PASSWORD: "{{ vcenter_password }}"
VMWARE_VALIDATE_CERTS: "true"
tasks:
- name: Resolve the cluster MoID
vmware.vmware_rest.vcenter_cluster_info:
filter_names: ["Production-Cluster-A"]
register: cl
- name: Resolve the datastore MoID
vmware.vmware_rest.vcenter_datastore_info:
filter_names: ["vsanDatastore"]
register: ds
- name: Resolve the VM folder MoID
vmware.vmware_rest.vcenter_folder_info:
filter_type: VIRTUAL_MACHINE
filter_names: ["Production"]
register: fld
- name: Create the VM through the REST API
vmware.vmware_rest.vcenter_vm:
placement:
cluster: "{{ cl.value[0].cluster }}"
datastore: "{{ ds.value[0].datastore }}"
folder: "{{ fld.value[0].folder }}"
name: rest-demo-01
guest_OS: RHEL_9_64
hardware_version: VMX_20
cpu: { count: 2, cores_per_socket: 1 }
memory: { size_MiB: 4096, hot_add_enabled: true }
state: present
Notice what is missing: there is no template:, no customization:, no wait_for_ip_address. vcenter_vm builds an empty VM shell. Cloning from a template and OS customization in the REST world go through the content library (vmware.vmware_rest.vcenter_vmtemplate_library_items and the deploy modules) — which is exactly why most teams provisioning from templates stay on community.vmware.vmware_guest, and reach for vmware.vmware_rest for the appliance / content-library / newest-feature tasks the community modules do not cover.
Install both when you want the choice:
python3 -m pip install --user aiohttp # vmware.vmware_rest needs this
ansible-galaxy collection install vmware.vmware_rest vmware.vmware
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.
Going deeper
Everything above is enough to run a fleet. This section is the material that separates “it worked in the lab” from “it survives 500 clusters and an auditor.”
Idempotency: why vmware_guest keeps saying changed
Ansible’s core promise is idempotency — run it twice, the second run reports ok and changes nothing. community.vmware mostly honours this, but vmware_guest has well-known soft spots, because it compares a desired spec against a live vSphere object whose representation does not round-trip cleanly:
- Annotation / notes — trailing newlines or encoding differences make the module think the note changed. Keep annotations short and stable, or set them once and drop the field from steady-state runs.
custom_attributes— referencing an attribute key that is not defined at the vCenter level, or sending a value vCenter stores differently, produces a perpetual diff. Pre-create the attribute definitions.- Disk / NIC ordering — the module matches disks and NICs by position. Reorder your
disk:ornetworks:list and it “sees” a change. Keep list order stable. customization:only applies at clone time. This is the big one: pointingvmware_guestat an existing VM will not re-run guest customization. Hostname/IP/domain are baked in on first boot from the template. To change them later you drive the guest OS directly (over SSH/WinRM), not vCenter.
Diagnose with -vv (or --diff where supported) and compare the reported before/after. When a field is inherently noisy, the cleanest fix is to split it into a separate, run-once play rather than fighting the diff on every convergence.
Check mode and diff mode are uneven
--check (dry run) and --diff support are per-module in community.vmware, not collection-wide. Read-heavy *_info modules are naturally safe; mutating modules vary — some fully support check mode, some ignore it, a few error. Never assume: test each module you rely on with --check against a scratch object before you trust a dry run in a change window. For vmware.vmware_rest, check-mode support is thinner still, because the modules are generated wrappers.
There is no persistent connection — every task logs in fresh
Each community.vmware task opens a new pyvmomi session (a SOAP login to vCenter), does its work, and tears it down. There is no connection pooling across tasks. On a 500-iteration workload that login/logout overhead dominates wall-clock time, and it shows up as a flood of login/logout events in vCenter. Mitigations:
- Prefer one task with a
loop:over many near-identical tasks — it keeps the code tight and the play overhead low. - Use the
async/poll: 0fan-out shown earlier so logins happen in parallel across forks rather than serially. - For pure reads at scale,
vmware_vm_inventorydoes a single bulk PropertyCollector query instead of N logins — always cheaper than loopingvmware_guest_info. vmware.vmware_restreuses an aiohttp session within a module run but still authenticates per task; the trade-offs are similar.
vmware_vm_inventory at scale
On a big vCenter the inventory plugin can be slow or memory-hungry if you pull everything. Tune it:
- Constrain
properties:to exactly the fields you use (the example earlier lists six — that is the point). Fetching every property of every VM on a 10,000-VM vCenter is minutes and gigabytes. - Filter server-side with
filters:(runtime.powerState == 'poweredOn') so vCenter does the culling, not your control node. - Enable caching — set
cache: truewith acache_plugin(e.g.jsonfile) and a TTL so repeatedansible-inventory/ansible-playbookruns in a pipeline do not re-query vCenter every time. - Watch
with_tags: true: pulling tags is a second REST round-trip per object and roughly doubles inventory time. Only enable it if you actually group or filter by tag.
Scale and concurrency: the real ceiling is vCenter, not Ansible
You can set forks: 100, but vCenter and the storage layer will not thank you:
- vCenter serializes and throttles concurrent provisioning operations. A safe production starting point is 5–10 concurrent clones; push higher only with vCenter HA and fast storage, and measure.
- Clone contention on the source template is real — fifty full clones all reading the same template VMDK hammer one datastore. Options: keep multiple copies of hot templates across datastores; use linked clones (share blocks, near-instant, but tie the clone’s lifetime to the template) for ephemeral dev/test; or instant clones for very fast, memory-shared forks where the workload suits it.
- Match
forks(or playserial:) to what the storage sustains, not what the control node can spawn. Aserial: 5play is often faster end-to-end thanforks: 50that trips datastore-latency alarms.
Security, the parts people skip
no_log: trueon any task that interpolates a password (domain-join, local-admin, product keys) so-vvvand AWX/AAP job output do not leak it.- For
vmware.vmware_rest, theVMWARE_*environment-variable pattern keeps creds out of task args entirely — pair it with a Vault-sourcedenvironment:block or AAP credential injection. - The automation SSO user should hold a custom role, not a cloned Administrator: VM provisioning, resource-pool and datastore access, tag assignment — and nothing else. Every action is a vCenter Event; forward those to your SIEM so “who cloned 400 VMs at 2 a.m.” is answerable.
validate_certs: falseis a lab-only crutch. In production it disables MITM protection on a channel carrying vCenter admin credentials. Install the vCenter CA chain on the control node instead.
Version and API caveats
- Pin your collections.
community.vmwarehas renamed and removed parameters across major versions (the 3.x → 4.x → 5.x line dropped deprecated aliases and reshaped a few modules). Pincommunity.vmware: 5.xinrequirements.ymland upgrade deliberately, reading the changelog. - Pin
pyvmomito a version that matches your vSphere generation; a too-old pyvmomi against vSphere 8 can miss APIs, a too-new one can warn. vmware.vmware_restneedsaiohttpand vSphere 7.0.2+; on 6.7 most of its VM lifecycle is unavailable.- Broadcom era: VMware is now Broadcom, licensing and bundling have shifted, and the supported-content story is consolidating toward
vmware.vmware. None of yourcommunity.vmwareplaybooks stop working — but when you plan a two-year automation roadmap, watch which collection your enterprise-support contract actually covers.
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.
Common beginner mistakes
These are conceptual traps — wrong mental models — distinct from the symptom→cause→fix table above.
-
“Ansible SSHes into the VM (or into ESXi) to build it.” No. During provisioning the play runs on
localhostand makes API calls to vCenter over HTTPS. The target VM does not exist yet and is never in your SSH inventory for this phase. Right model: provisioning talks to vCenter; configuration (a later, separate play) talks to the VMs. -
“I’ll point
vmware_guestat an existing VM to change its hostname or IP.” Customization only fires when a VM is cloned from a template. Re-running the module on a live VM will not re-customize it. Right model: OS-level identity is set once at birth; change it afterward by driving the guest OS, not vCenter. -
“
changed: truemeans my playbook did real work, or is broken.”vmware_guestproduces spurious diffs from annotations, custom attributes, and list ordering. Right model: a stable steady-state run should beok; if it is perpetuallychanged, hunt the noisy field — do not assume the VM was rebuilt. -
“Snapshots are backups.” A snapshot is a delta on top of a running disk. Chains grow, hurt I/O, and vanish if the base VM is lost. Right model: snapshots are short-term undo before a risky change — take, validate, delete. Durable recovery is Veeam/Rubrik/replication.
-
“
validate_certs: falseis just a lab habit, harmless in prod.” It disables TLS verification on a session carrying vCenter admin credentials — textbook MITM exposure. Right model: it is lab-only; production installs the vCenter CA chain and setstrue. -
“
vmware.vmware_restis a drop-in replacement forcommunity.vmware.” Different connection params (vcenter_hostnamevshostname), a different SDK (aiohttp vs pyvmomi), and you must resolve MoIDs before creating objects. Right model: it is a different collection for REST-native and newest-feature work, not a rename of the community one. -
“More VMs to build? Just raise
forksto 100.” The bottleneck is vCenter and the datastore, not Ansible’s fork count. Right model: 5–10 concurrent clones is a sane ceiling; scale by spreading templates and datastores and measuring storage latency, not by cranking forks. -
“One golden template per OS, built once, forever.” Templates rot — unpatched, drifting from the current baseline. Right model: rebuild templates on a cadence (monthly), version them, and reprovision rather than in-place upgrade.
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.
Practice challenges
Work these in order — each builds on the last. Try before opening the solution.
Challenge 1 (Beginner): Confirm you can reach vCenter
Write a one-play playbook that prints the vCenter version and build number. Nothing should change on the server.
<details> <summary>Solution</summary>
- hosts: localhost
gather_facts: false
tasks:
- name: Query vCenter
community.vmware.vmware_about_info:
hostname: "{{ vcenter_hostname }}"
username: "{{ vcenter_username }}"
password: "{{ vcenter_password }}"
validate_certs: true
register: about
- name: Show version
ansible.builtin.debug:
msg: "vCenter {{ about.about_info.version }} build {{ about.about_info.build }}"
Why: vmware_about_info is read-only — the fastest way to prove auth, DNS, and cert trust all work before you touch anything mutating.
</details>
Challenge 2 (Beginner): Power-cycle one VM
Gracefully restart a VM called web-01 (in folder Production) using the dedicated power module, not vmware_guest.
<details> <summary>Solution</summary>
- name: Restart guest OS
community.vmware.vmware_guest_powerstate:
hostname: "{{ vcenter_hostname }}"
username: "{{ vcenter_username }}"
password: "{{ vcenter_password }}"
validate_certs: true
name: web-01
folder: "/Primary-DC/vm/Workloads/Production"
state: reboot-guest # graceful, via VMware Tools
Why: reboot-guest asks VMware Tools for a clean OS reboot; restarted would be a hard power reset. folder: disambiguates a duplicate web-01 name.
</details>
Challenge 3 (Intermediate): Provision one VM with two disks
From tpl-rhel9-base, create data-01 with a 60 GB OS disk and a separate 200 GB thin data disk, powered on.
<details> <summary>Solution</summary>
- name: Provision a two-disk VM
community.vmware.vmware_guest:
hostname: "{{ vcenter_hostname }}"
username: "{{ vcenter_username }}"
password: "{{ vcenter_password }}"
validate_certs: true
name: data-01
template: tpl-rhel9-base
datacenter: Primary-DC
folder: "/Primary-DC/vm/Workloads/Production"
cluster: Production-Cluster-A
datastore: vsanDatastore
state: poweredon
disk:
- size_gb: 60
type: thin
unit_number: 0
- size_gb: 200
type: thin
unit_number: 1
hardware:
memory_mb: 8192
num_cpus: 4
Why: disk: is an ordered list; each entry needs a distinct unit_number (the SCSI ID). Omitting it on the second disk is a common cause of “changed every run.”
</details>
Challenge 4 (Intermediate): Safe snapshot around a change
Take a quiesced snapshot before a change and remove it after, so re-running the play does not pile up snapshots.
<details> <summary>Solution</summary>
- name: Pre-change snapshot
community.vmware.vmware_guest_snapshot:
hostname: "{{ vcenter_hostname }}"
username: "{{ vcenter_username }}"
password: "{{ vcenter_password }}"
validate_certs: true
name: web-01
datacenter: Primary-DC
folder: "/Primary-DC/vm/Workloads/Production"
state: present
snapshot_name: pre-change
quiesce: true
memory_dump: false
# ... risky change here ...
- name: Remove the snapshot
community.vmware.vmware_guest_snapshot:
hostname: "{{ vcenter_hostname }}"
username: "{{ vcenter_username }}"
password: "{{ vcenter_password }}"
validate_certs: true
name: web-01
datacenter: Primary-DC
folder: "/Primary-DC/vm/Workloads/Production"
state: absent
snapshot_name: pre-change
Why: A fixed snapshot_name with state: present then absent is idempotent — present no-ops if the snapshot exists, absent no-ops if it is already gone. quiesce: true gives a filesystem-consistent point (needs VMware Tools).
</details>
Challenge 5 (Advanced): Live inventory of prod VMs, then ping
Build a dynamic inventory that returns only powered-on VMs tagged production, grouped by tag, and ping them.
<details> <summary>Solution</summary>
# inventory/prod.vmware.yml
plugin: community.vmware.vmware_vm_inventory
hostname: vcsa.corp.example.com
username: "{{ lookup('env', 'VCENTER_USERNAME') }}"
password: "{{ lookup('env', 'VCENTER_PASSWORD') }}"
validate_certs: true
with_tags: true
properties:
- config.name
- guest.ipAddress
- runtime.powerState
- tag
filters:
- "runtime.powerState == 'poweredOn'"
- "'production' in tag"
compose:
ansible_host: guest.ipAddress
keyed_groups:
- key: tag
prefix: tag
ansible-inventory -i inventory/prod.vmware.yml --graph
ansible -i inventory/prod.vmware.yml tag_production -m ping
Why: Filtering server-side (filters:) and limiting properties: keeps the query cheap on a big vCenter; the tag_production group is generated by keyed_groups.
</details>
Challenge 6 (Advanced): Create the same VM via the REST collection
Build an empty rest-demo-01 shell with vmware.vmware_rest, resolving the cluster/datastore/folder MoIDs first.
<details> <summary>Solution</summary>
- hosts: localhost
gather_facts: false
environment:
VMWARE_HOST: "{{ vcenter_hostname }}"
VMWARE_USER: "{{ vcenter_username }}"
VMWARE_PASSWORD: "{{ vcenter_password }}"
VMWARE_VALIDATE_CERTS: "true"
tasks:
- name: Resolve cluster MoID
vmware.vmware_rest.vcenter_cluster_info:
filter_names: ["Production-Cluster-A"]
register: cl
- name: Resolve datastore MoID
vmware.vmware_rest.vcenter_datastore_info:
filter_names: ["vsanDatastore"]
register: ds
- name: Resolve VM folder MoID
vmware.vmware_rest.vcenter_folder_info:
filter_type: VIRTUAL_MACHINE
filter_names: ["Production"]
register: fld
- name: Create the VM through REST
vmware.vmware_rest.vcenter_vm:
placement:
cluster: "{{ cl.value[0].cluster }}"
datastore: "{{ ds.value[0].datastore }}"
folder: "{{ fld.value[0].folder }}"
name: rest-demo-01
guest_OS: RHEL_9_64
hardware_version: VMX_20
cpu: { count: 2, cores_per_socket: 1 }
memory: { size_MiB: 4096, hot_add_enabled: true }
state: present
Why: REST modules are MoID-based — you must look up managed-object IDs with *_info modules before vcenter_vm can place the VM. Contrast with vmware_guest, which resolves names for you.
</details>
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?
- Which connection parameter differs between
community.vmwareandvmware.vmware_rest? - Name two reasons
vmware_guestmight reportchanged: trueon an unchanged VM.
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 the vSphere SOAP API; what
community.vmwareimports at runtime. - aiohttp — the async HTTP library that
vmware.vmware_restuses to call vCenter’s REST API (in place of pyvmomi’s SOAP). - 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.
- MoID (Managed Object ID) — vCenter’s internal identifier for an object, e.g.
vm-1042ordomain-c7. Thevmware.vmware_restcollection places objects by MoID, not by name — you resolve them first with*_infomodules. - Template — a VM converted to a read-only master image, used as the source for clones. The heart of the “build once, provision many” pattern.
- Linked clone — a clone that shares disk blocks with its source; fast and space-efficient, but its lifetime is tied to the source.
- Instant clone — a running-memory fork of a live VM; near-instant, shares memory pages until the clones diverge.
- Content Library — vCenter’s store of templates, ISOs and OVFs; the REST collection deploys templates from here.
- PropertyCollector — the vSphere API mechanism for bulk-fetching object properties in a single call; what
vmware_vm_inventoryuses to stay fast at scale. - SPBM — Storage Policy-Based Management; policy-driven datastore placement, managed with
vmware_storage_policy. - VMware Tools — the in-guest agent that reports the IP, enables graceful shutdown/reboot, quiesced snapshots, and OS customization.
- Quiesce — flushing guest filesystem buffers before a snapshot so the image is consistent (requires VMware Tools).
- community.vmware / vmware.vmware_rest / vmware.vmware — the three VMware collections: the broad pyvmomi community one, the REST/aiohttp certified one, and the newer curated/supported one, respectively.
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, Hybrid & multi-cloud orchestration, covers 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.