Skip to content

Azure Inventory

Dynamic inventory is pulled from Azure using the azure.azcollection.azure_rm plugin, configured in inventory.azure_rm.yml.

All inventory files are registered in ansible-azure.cfg (Azure's config file, not ansible.cfg) so no -i flag is needed on any command — including any additional subscription inventories:

# ansible-azure.cfg
[defaults]
inventory = inventory.azure_rm.yml,inventory.group_priority.yml

Current configuration

# inventory.azure_rm.yml
plugin: azure.azcollection.azure_rm
subscription_id: <subscription-id>
auth_source: msi

conditional_groups:
  azure_rm: true
  has_attached_disks: "data_disks | count > 0"
  platform_azure: true
  os_windows: "os_profile.system == 'windows'"
  os_linux: "os_profile.system == 'linux'"

hostnames:
  - name

keyed_groups:
  - prefix: azure_rm
    key: os_profile.system
  # Application tier — keyed off the `application` VM tag (app_kuiper, app_nginx, …)
  - key: tags.application
    default_value: unclassified
    prefix: "app"
    separator: "_"
  # Environment tiers — <env>_azure_app_<app> and <env>_azure_platform, keyed off the
  # `environment` tag (see Application & Environment Groups for the full expressions)
  - key: >-
      (tags.environment | default('unknown') | regex_replace('-', '_'))
      ~ '_azure_app_' ~ (tags.application | default('unclassified'))
  - key: >-
      (tags.environment | default('unknown') | regex_replace('-', '_'))
      ~ '_azure_platform'

# Restrict inventory to one environment's VMs
include_host_filters:
  - tags.environment | default('') == 'ire-copier'

compose:
  application: tags.application | default('unclassified')

Key settings

auth_source: msi — authenticates using a managed identity. No credentials to rotate or store.

hostnames — uses the VM's Azure name directly as the Ansible inventory hostname.

conditional_groups — creates groups based on boolean expressions evaluated against each VM's facts:

Group Condition Purpose
azure_rm always true Legacy — every Azure VM (superseded by platform_azure)
has_attached_disks has one or more data disks VMs with attached storage beyond the OS disk
platform_azure always true Every Azure VM — cloud tier of the group taxonomy
os_windows os_profile.system == 'windows' Windows VMs
os_linux os_profile.system == 'linux' Linux VMs

os_profile.system already returns exactly linux/windows, so the OS groups compare it directly (unlike AWS, which matches a substring of platform_details).

keyed_groups — the first entry creates the legacy azure_rm_linux / azure_rm_windows groups (prefixed with azure_rm, derived from os_profile.system), kept for --limit backward-compat; new work should target os_linux / os_windows. The remaining entries build the tag-driven app_* and <env>_azure_* groups from the application and environment VM tags — the application and environment tiers of the group taxonomy, detailed in Application & Environment Groups.

include_host_filters — scopes the inventory to a single environment by tag (tags.environment == 'ire-copier'), so only that environment's VMs are discovered. subscription_id pins discovery to one subscription. Without a filter, all accessible VMs the managed identity can read would be included.

Authentication

Authentication uses a user-assigned managed identity associated with the Ansible controller VM or container. No credentials, secrets, or service principal details are required. The managed identity must have the Reader role assigned at the subscription level (or at each resource group level if scoping to specific groups).

To verify the managed identity can reach the Azure API:

az account show
az vm list --output table

Multiple Azure subscriptions

When managing VMs across multiple Azure subscriptions, create a separate inventory file for each additional subscription with subscription_id set. The managed identity's default subscription is accessed automatically — subscription_id is only required for subscriptions beyond the default.

For each additional subscription, create a dedicated inventory file:

# inventory.azure_rm_sub2.yml
plugin: azure.azcollection.azure_rm

auth_source: msi
subscription_id: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

conditional_groups:
  azure_rm: true
  has_attached_disks: "data_disks | count > 0"
  platform_azure: true
  os_windows: "os_profile.system == 'windows'"
  os_linux: "os_profile.system == 'linux'"

hostnames:
  - name

keyed_groups:
  - prefix: azure_rm
    key: os_profile.system

Add each file to ansible-azure.cfg alongside the default inventory:

# ansible-azure.cfg
[defaults]
inventory = inventory.azure_rm.yml,inventory.azure_rm_sub2.yml,inventory.group_priority.yml

The managed identity must have the Reader role in each additional subscription for discovery to succeed.

Verifying the inventory

# List all discovered Azure hosts
ansible-inventory --list --yaml

# Show the full group tree
ansible-inventory --graph

# Inspect variables for a specific host
ansible-inventory --host my-azure-vm

Application & environment groups

Beyond platform and OS, inventory.azure_rm.yml also builds the app_* and <env>_azure_* groups directly from the application and environment VM tags (the keyed_groups/compose shown above) — this is how groups like app_kuiper and ire_copier_azure_platform are created and used as --limit targets and group_vars scopes. There is no separate constructed inventory; see Application & Environment Groups for the full details. Precedence between the tiers is set by inventory.group_priority.yml — see Group priority.