Part 14 – VMware Automation with AAP: Managing VM Hardware

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

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"

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

---
- 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

(Visited 7 times, 1 visits today)

By Ash Thomas

Ash Thomas is a seasoned IT professional with extensive experience as a technical expert, complemented by a keen interest in blockchain technology.