Instead of clicking around vCenter, we write a single Ansible playbook that handles any VM. AWX gives it a web form (called a survey) so anyone can run it without touching the command line.

Each playbook does one thing. No hardcoded VM names and everything comes in as a variable at runtime so we will add playbooks in this format.
playbooks/
├── get_all_vms.yml ← inventory
├── get_vm_info.yml ← VM details
├── vm_hardware.yml ← change RAM/CPU
├── vm_snapshot.yml ← snapshot management
├── vm_power.yml ← power on/off/restart
├── vm_deploy.yml ← deploy from template
├── vm_delete.yml ← delete VM
├── vm_network.yml ← change network
└── vm_disk.yml ← add/expand disk
Add a new playbook to Get all VM Inventory
playbooks/
├── get_all_vms.yml ← inventory

Run the playbook
ansible-playbook /etc/ansible/playbooks/get_all_vms.yml
Sort by just the VM name
ansible-playbook /etc/ansible/playbooks/get_all_vms.yml | grep "msg"
Add a new playbook to Get all VM Info
playbooks/
├── get_vm_info.yml ← VM details

Run the playbook get_vm_info.yml
ansible-playbook get_vm_info.yml -e "vm_name=ad01"### `-e` is the Same as AWX Survey and is extra vars and injects vm_name=ad01 into playbook

Add a new playbook to Changing RAM/CPU
playbooks/
├── vm_hardware.yml ← change RAM/CPU
---
- name: VM Hardware Management
hosts: localhost
gather_facts: false
connection: local
become: false
tasks:
- name: Validate RAM input
fail:
msg: "RAM must be between 1GB and 512GB! You entered {{ ram_gb }}GB"
when: ram_gb is defined and (ram_gb | int < 1 or ram_gb | int > 512)
- name: Validate CPU input
fail:
msg: "CPUs must be between 1 and 128! You entered {{ cpu }}"
when: cpu is defined and (cpu | int < 1 or cpu | int > 128)
- name: Get current VM hardware
community.vmware.vmware_guest_info:
hostname: "{{ lookup('env', 'VMWARE_HOST') }}"
username: "{{ lookup('env', 'VMWARE_USER') }}"
password: "{{ lookup('env', 'VMWARE_PASSWORD') }}"
validate_certs: false
datacenter: "Datacenter"
name: "{{ vm_name }}"
register: vm_info
- name: Show BEFORE hardware
debug:
msg:
- "=== CURRENT STATE ==="
- "VM Name : {{ vm_info.instance.hw_name }}"
- "CPUs : {{ vm_info.instance.hw_processor_count }}"
- "RAM : {{ (vm_info.instance.hw_memtotal_mb / 1024) | int }}GB"
- "Power : {{ vm_info.instance.hw_power_status }}"
- name: Show PLANNED changes
debug:
msg:
- "=== CHANGES TO BE APPLIED ==="
- "RAM : {{ (vm_info.instance.hw_memtotal_mb / 1024) | int }}GB → {{ ram_gb }}GB"
- "CPUs : {{ vm_info.instance.hw_processor_count }} → {{ cpu | default(vm_info.instance.hw_processor_count) }}"
- name: Fail if changing CPU while powered on
fail:
msg: "{{ vm_name }} is powered on! Power off VM before changing CPU."
when:
- cpu is defined
- vm_info.instance.hw_power_status == "poweredOn"
- name: Change VM hardware
community.vmware.vmware_guest:
hostname: "{{ lookup('env', 'VMWARE_HOST') }}"
username: "{{ lookup('env', 'VMWARE_USER') }}"
password: "{{ lookup('env', 'VMWARE_PASSWORD') }}"
validate_certs: false
datacenter: "Datacenter"
name: "{{ vm_name }}"
hardware:
memory_mb: "{{ ram_gb | int * 1024 if ram_gb is defined else omit }}"
num_cpus: "{{ cpu | int if cpu is defined else omit }}"
state: present
- name: Wait for vCenter to apply changes
pause:
seconds: 5
- name: Get updated VM hardware
community.vmware.vmware_guest_info:
hostname: "{{ lookup('env', 'VMWARE_HOST') }}"
username: "{{ lookup('env', 'VMWARE_USER') }}"
password: "{{ lookup('env', 'VMWARE_PASSWORD') }}"
validate_certs: false
datacenter: "Datacenter"
name: "{{ vm_name }}"
register: vm_info_after
- name: Show AFTER hardware
debug:
msg:
- "=== AFTER ==="
- "VM Name : {{ vm_info_after.instance.hw_name }}"
- "CPUs : {{ vm_info_after.instance.hw_processor_count }}"
- "RAM : {{ (vm_info_after.instance.hw_memtotal_mb / 1024) | int }}GB"
- "Power : {{ vm_info_after.instance.hw_power_status }}"
Run the playbook vm_hardware.yml
ansible-playbook vm_hardware.yml-e "vm_name=ad01" ### `-e` is the Same as AWX Survey and is extra vars and injects vm_name=ad01 into playbook

This is good as it picks the hot add is not enabled.

Lets try that on our Centos OS VM

The ram is changed as expected


Lets run the git to see what changed upload them all to our Project repo
wk@ansible:/etc/ansible$ git status
On branch main
Your branch is up to date with 'origin/main'.
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: ansible.cfg
modified: playbooks/get_all_vms.yml
Untracked files:
(use "git add <file>..." to include in what will be committed)
.vmware_env
playbooks/get_all_vms.yml.old
no changes added to commit (use "git add" and/or "git commit -a")
awk@ansible:/etc/ansible$ g
Stage all changes
awk@ansible:/etc/ansible$ git add .
awk@ansible:/etc/ansible$git commit -m “Description of changes”
awk@ansible:/etc/ansible$ git commit -m "Added new playbook for awx"
[main 738073a] Added new playbook for awx
4 files changed, 32 insertions(+), 10 deletions(-)
create mode 100644 .vmware_env
create mode 100644 playbooks/get_all_vms.yml.old
awk@ansible:/etc/ansible$Push to GitLab
awk@ansible:/etc/ansible$ git push origin main
Username for 'http://gitlab.ash.local': awk
Password for 'http://awk@gitlab.ash.local':
Enumerating objects: 10, done.
Counting objects: 100% (10/10), done.
Delta compression using up to 4 threads
Compressing objects: 100% (6/6), done.
Writing objects: 100% (6/6), 687 bytes | 343.00 KiB/s, done.
Total 6 (delta 4), reused 0 (delta 0), pack-reused 0
To http://gitlab.ash.local/awk/vmware-automation.git
f1a0e84..738073a main -> main
awk@ansible:/etc/ansible$
AWX → Projects → VMware Automation → Sync

ansible-playbook /etc/ansible/playbooks/get_vm_info.yml -e "vm_name=ad01"
Once this is working, we can move the playbook to AWX so users can execute it

So that’s the workflow of creating playbooks and pushing it across to AAP

