Ansible Lesson 31 of 42

Ansible for VMware vSphere & NSX, In Depth: community.vmware, vCenter Automation, VM Templates & Software-Defined Networking

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:

Prerequisites

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

Security Notes

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

  1. Which Python library does community.vmware depend on?
  2. What’s the canonical pattern for provisioning a VM?
  3. What does wait_for_ip_address: true do?
  4. Which module manages VM snapshots?
  5. What’s the difference between vmware_dvswitch and vmware_vswitch?
  6. Which inventory plugin pulls live inventory from vCenter?
  7. What does quiesce: true mean on a snapshot?
  8. Which collection ships NSX-T modules?

Exercise

Build a complete role vsphere_app_stack that:

  1. Provisions 3 application VMs and 1 database VM from tpl-rhel9-base.
  2. Places app VMs in a “App-Tier” resource pool, DB VM in “DB-Tier”.
  3. Creates a dvSwitch portgroup PG-App-Tier (VLAN 200) if missing.
  4. Tags every VM with Ansible-Managed, App: myapp, and Environment: prod.
  5. Snapshots all VMs before applying changes.
  6. Configures cluster HA settings (admission control, restart priority).
  7. Exports a dynamic inventory grouping by tags.
  8. Includes a validate.yml task 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

Glossary

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.

ansiblevmwarevspherevcenternsxcommunity-vmwareesxivm-templatesdatacenterkloudvin
Need this built for real?

Vinod is a Senior Cloud Architect (22+ yrs) — available for Azure / AWS / GCP architecture, landing zones, and migrations.

Work with me

Comments