Python scripts and vRO workflows that build the Ansible inventories used by the Windows post-deploy pipeline. Two source systems are supported — VMware vSphere and HPE OneView — each with a Python generator that runs on the Ansible control node and a vRO workflow that triggers it on demand.
These run upstream of the Windows post-deploy automation. The inventories they produce are what ansible-playbook -i inventories/Prod/... reads when Aria kicks off windows_postdeploy.yml for a new VM.
Ansible_Inventory_Generators/
├── README.md This file
├── vsphere/
│ ├── Create-ansible-inventory-from-vSphere.py Python generator (lives on Ansible control node)
│ ├── Get-all-VMs-from-vCenter.js vRO scriptable task (triggers the Python script)
│ └── push_inventory_to_bitbucket.js vRO scriptable task (commits inventory to Bitbucket)
├── oneview/
│ ├── generate_oneview_inventory.py Python generator for OneView 6.6, 8.9, 10.0
│ └── vro_workflow.js vRO scriptable task (triggers the Python script)
└── vro-package/
└── ansible_inventory_generators.package Signed vRO .package file — importable as-is into vRO
┌─────────────────────────────────────────────────────────────────┐
│ Trigger: operator runs the vRO workflow from Assembler │
│ - "Generate Ansible Inventory from vSphere" │
│ - "Generate Ansible Inventory from OneView" │
└─────────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────┐
│ vRO scriptable task opens SSH to ansible.example.com │
│ Runs python3 /home/ansible/inventories/{vsphere|oneview}/ │
│ generate_*.py --<args> >logs/inventory_<timestamp>.log │
└─────────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────┐
│ Python script connects to the source system (vCenter or OneView)│
│ Enumerates VMs/servers, groups by OS / cluster / folder / HW │
│ Writes INI-format inventory file to /home/ansible/inventories/ │
└─────────────────────────────────────────────────────────────────┘
↓ (vSphere only, optional step)
┌─────────────────────────────────────────────────────────────────┐
│ Second scriptable task: push_inventory_to_bitbucket │
│ git add / commit / push to github.com/noahfarshad │
│ Version-controlled history of every inventory generation │
└─────────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────┐
│ Ansible reads the generated inventory on next playbook run │
│ windows_postdeploy.yml -> --inventory inventories/Prod/... │
└─────────────────────────────────────────────────────────────────┘
The generators run on a cadence (scheduled or manual) or when the environment changes enough that the inventory would drift from reality. They are not called during a VM deployment — the post-deploy playbook uses the already-present inventory file at /home/ansible/production/ansible/playbooks/inventories/Prod/hosts.
Location on control node: /home/ansible/inventories/vsphere/generate_vsphere_inventory.py
Language: Python 3
Dependencies: pyvmomi, PyYAML
Author: Noah Farshad
What it does: Connects to a vCenter, enumerates every VM (powered-on and off, unless filtered), extracts IP addresses, folder path, cluster, OS guest identity, and resource pool metadata. Groups VMs into Ansible inventory groups by folder hierarchy, OS type, and environment. Writes an INI-format inventory file.
Inputs:
| Argument | Required | Purpose |
|---|---|---|
--vcenter-host |
yes | vCenter FQDN or IP |
--vcenter-user |
yes | Username for vCenter auth |
--vcenter-password |
yes | Password |
--inventory-dir |
no | Output directory (default: /home/ansible/inventories/vsphere) |
--environment |
no | Environment label (dev/test/prod), applied to group names (default: prod) |
--skip-templates |
no | Omit VM templates from the inventory |
--require-ip |
no | Only include VMs that have a guest IP (skips powered-off VMs without cached IPs) |
--skip-powered-off |
no | Omit powered-off VMs entirely |
--validate-certs |
no | Validate vCenter SSL cert (default: false) |
How IPs are resolved: Iterates vm.guest.net, collects all IPv4 addresses, filters out link-local (169.254.x.x) and IPv6. Multiple IPs per VM land as separate inventory attributes.
How groups are built: VM folder path drives the primary group name (e.g., TX-CORP-IT/AUTH/PRD becomes tx_corp_it_auth_prd). OS guest family gives a secondary group (windows, linux). All VMs also land in an all group for broad playbook targeting.
Context: vRO scriptable task, first element in the "Generate Ansible Inventory from vSphere" workflow. Purpose: SSH into the Ansible control node and execute the Python script with parameters passed from the vRO workflow form.
Inputs (from the vRO form):
| Parameter | Type | Example |
|---|---|---|
vCenter |
VC:SdkConnection |
Selected from the vRO inventory dropdown |
vCenter_username |
string | administrator@vsphere.local |
vCenter_password |
SecureString | — |
ansible_password |
SecureString | SSH password for the ansible user on the control node |
What it does:
- Extracts the vCenter hostname from the
VC:SdkConnectionobject - Derives the environment name from the vCenter short name (first DNS label)
- Opens an
SSHSessiontoansible.example.comas useransible - Cleans up old
/tmp/inventory_output_*.logfiles (keeps the latest 10) - Runs the Python script with the parsed arguments, redirecting output to a timestamped log
- Outputs the inventory file path and log file path for downstream workflow use
Hardcoded defaults:
- Ansible host:
ansible.example.com - Ansible user:
ansible - Python binary:
python3.11 - Script path:
/home/ansible/inventories/vsphere/generate_vsphere_inventory.py
Change these at the top of the script if the control node is reconfigured.
Context: vRO scriptable task, second (optional) element in the vSphere inventory workflow. Purpose: Commit the freshly-generated inventory to the Essential Coach Bitbucket repo for version control.
Inputs:
| Parameter | Type | Example |
|---|---|---|
vCenter |
VC:SdkConnection |
Used to derive commit message |
ansibleHost |
string | ansible.example.com |
ansibleUsername |
string | ansible |
ansible_password |
SecureString | SSH password |
bitbucketToken |
SecureString | Bitbucket API token for git push auth |
bitbucketRepoUrl |
string | github.com/noahfarshad/scm/hsia/vmware-automation-ansible.git |
What it does:
- SSHes to the Ansible control node
- Runs
git add,git commit -m "Inventory update for <vcenter> at <timestamp>", andgit pushagainst the Bitbucket repo using the API token - Returns
changesPushed: true/falseso the workflow can report whether anything actually changed - Handles the no-op case (inventory identical to last commit) gracefully
Safety: The token is a SecureString and is passed into the git remote URL only at runtime — not stored on disk.
Location on control node: /home/ansible/inventories/oneview/generate_oneview_inventory.py
Language: Python 3
Dependencies: requests, PyYAML, urllib3
Supports: HPE OneView versions 6.6, 8.9, and 10.0
Why three versions: Essential Coach runs a mixed OneView fleet. Each version has a different required X-API-Version header value:
| OneView Version | X-API-Version |
|---|---|
| 6.6 | 3800 |
| 8.9 | 6400 |
| 10.0 | 7600 |
The script reads --version and maps it to the right header automatically via the API_VERSIONS dict.
Inputs:
| Argument | Env var | Required | Purpose |
|---|---|---|---|
--oneview-host |
ONEVIEW_HOST |
yes | OneView appliance FQDN |
--oneview-user |
ONEVIEW_USER |
yes | Username (prefix local\ for local accounts, domain\ for AD) |
--oneview-password |
ONEVIEW_PASSWORD |
yes | Password |
--version |
ONEVIEW_VERSION |
yes | OneView version: 6.6, 8.9, or 10.0 |
--local-account |
— | no | Boolean — use OneView local account (default true) |
--output-dir |
INVENTORY_DIR |
no | Where to write the inventory file |
What it does:
- Authenticates via
/rest/login-sessionswith the appropriate API version header - Enumerates server hardware (
/rest/server-hardware) and server profiles (/rest/server-profiles) - Extracts iLO addresses, hostnames, server models, power state, and profile templates
- Groups servers by model, enclosure, and profile template into Ansible inventory groups
- Writes YAML-format inventory (OneView inventories tend to be smaller and more structured than vSphere; YAML is easier to read for this use case)
Authentication variants:
- Local OneView account (default):
Administratoron the OneView appliance itself - Directory-integrated: prefix the username with the directory name (e.g.,
corp.example.com\ansible-svc)
Context: vRO scriptable task — the single element inside the "Generate Ansible Inventory from OneView" workflow. Purpose: SSH into the control node and trigger the OneView Python generator.
Inputs (from the vRO form):
| Parameter | Type | Example |
|---|---|---|
oneviewHost |
string | oneview-tx01.example.com |
oneviewUsername |
string | Administrator |
oneviewPassword |
SecureString | — |
oneviewVersion |
string | 8.9 (or 6.6 / 10.0) |
ansible_password |
SecureString | SSH password |
useLocalAccount |
boolean | true for local OneView admin, false for directory |
What it does:
- Validates all inputs (throws early if missing)
- Opens SSH to
ansible.example.comasansible - Derives a log file name from the OneView short name + ISO timestamp
- Runs
python3 /home/ansible/inventories/oneview/generate_oneview_inventory.pywith all arguments - Streams output back into vRO's
System.logfor visibility in the workflow run - Returns the inventory path, log path, and execution status (SUCCESS/FAILED)
vro-package/ansible_inventory_generators.package is the signed vRO export of the two workflows. It is the deployable artifact — import this into a fresh vRO and you get both workflows, their input forms, parameter definitions, and embedded scripts wired up exactly as they run today.
Contents (according to the export manifest):
| Workflow | UUID | Scripts |
|---|---|---|
| Generate Ansible Inventory from vSphere | eb56b31b-9e69-4c3d-b801-f942ced445b7 |
Get-all-VMs + push_inventory_to_bitbucket |
| Generate Ansible Inventory from OneView | 9fa43a16-caab-4679-9011-da9fde1533ae |
vro_workflow (single scripted task) |
Signed with: CN=vRO vco-app-67d64887c5-tx99n (VMware-issued certificate).
- vRO Client → Actions → Import Package
- Select
ansible_inventory_generators.package - Accept the signing certificate (the CN above is VMware's, not a forgery risk — but confirm with your security team)
- Resolve any conflicts if prior versions exist (usually "Keep Server" unless intentionally replacing)
- Both workflows appear under the default location; move them into your Essential Coach folder structure as needed
The raw .js and .py files in vsphere/ and oneview/ are the editable source — they're what a developer opens, edits, commits to git, and pastes into vRO when making changes. The .package file is the snapshot of what's currently deployed. If these drift, the source files in this folder are the ones to trust — the package should be re-exported after any script change.
# vSphere
pip3 install pyvmomi pyyaml
# OneView
pip3 install requests pyyaml urllib3/home/ansible/inventories/
├── vsphere/
│ └── generate_vsphere_inventory.py Copy of Create-ansible-inventory-from-vSphere.py
└── oneview/
└── generate_oneview_inventory.py Direct copy
Rename on deploy — the vRO scripts hardcode generate_vsphere_inventory.py (dropping the "Create-ansible-inventory-from-" prefix). The OneView script keeps its original name.
The ansible user on ansible.example.com must accept password auth from the vRO appliance's outbound IP. If SSH keys are used instead of passwords, the vRO scripts need to switch from session.connectWithPassword(...) to a key-based method.
- Aria Assembler → Extensibility → Workflows
- Pick "Generate Ansible Inventory from vSphere" or "Generate Ansible Inventory from OneView"
- Click Run
- Fill in the form (vCenter/OneView host, credentials, options)
- Submit — watch the logs in the Workflow Run tab
- Output: inventory file path + log file path
vSphere:
ssh ansible@ansible.example.com
cd /home/ansible/inventories/vsphere
python3.11 generate_vsphere_inventory.py \
--vcenter-host vcenter-tx01.example.com \
--vcenter-user administrator@vsphere.local \
--vcenter-password '<pw>' \
--environment prod \
--skip-templates \
--require-ipOneView:
ssh ansible@ansible.example.com
cd /home/ansible/inventories/oneview
python3 generate_oneview_inventory.py \
--oneview-host oneview-tx01.example.com \
--oneview-user Administrator \
--oneview-password '<pw>' \
--version 8.9 \
--local-accountSSH can't reach the control node or the password is wrong. Check:
- Network path from vRO appliance →
ansible.example.com(TCP 22) ansibleuser password in the workflow form matches the current password on the control node- SSH daemon is running:
sshd -ton the control node
Default behavior is to skip cert validation (--validate-certs is off by default on the vSphere script). If you've re-enabled it and hit this, the vCenter or OneView cert isn't in the control node's trust store. Either trust the cert or disable validation.
Wrong X-API-Version for the connected OneView. Double-check the --version value against the actual appliance version:
curl -sk https://<oneview>/rest/version | python3 -m json.toolMatch the appliance's currentVersion to the table in this README.
The source system has no matching objects, or filters are too strict. Remove --require-ip and --skip-powered-off to widen the net and see what's actually being discovered.
Token is wrong, expired, or lacks write access. Generate a new token in Bitbucket → Personal settings → App passwords with Repositories: Read, Write scope. Update the vRO workflow's bitbucketToken input to the new value.
The generator produced an inventory identical to the last commit. This is safe — the push script handles it with changesPushed: false in the output. No action needed.
By default the script includes them. If missing, check: was --skip-powered-off passed? Also, powered-off VMs without a cached guest IP get filtered out if --require-ip is set.
| Artifact | Lines | Notes |
|---|---|---|
Create-ansible-inventory-from-vSphere.py |
616 | Full vSphere inventory generator with IP/folder/OS grouping |
Get-all-VMs-from-vCenter.js |
133 | vRO scriptable task; hardcoded ansible host + script path |
push_inventory_to_bitbucket.js |
233 | vRO scriptable task; token-based git push |
generate_oneview_inventory.py |
682 | Supports OneView 6.6 / 8.9 / 10.0 via API version mapping |
vro_workflow.js |
245 | Single scriptable task for OneView workflow |
ansible_inventory_generators.package |
— | Signed vRO package containing both workflows |
All scripts were validated byte-identical between the desktop working copies and the signed customer vRO package export (see validation notes in the closeout chat).
Original author: Noah Farshad (noah@essential.coach) Engagement: VMware / Aria Automation reference implementation