Tutorial: Configure dynamic inventories of your Azure resources using Ansible

Important

Ansible 2.8 (or later) is required to run the sample playbooks in this article.

Caution

This article references CentOS, a Linux distribution that is nearing End Of Life (EOL) status. Please consider your use and plan accordingly. For more information, see the CentOS End Of Life guidance.

The Ansible dynamic inventory feature removes the burden of maintaining static inventory files.

In this tutorial, you'll use Azure's dynamic-inventory plug-in to populate your Ansible inventory.

In this article, you learn how to:

  • Configure two test virtual machines.
  • Add tags to Azure virtual machines
  • Generate a dynamic inventory
  • Use conditional and keyed groups to populate group memberships
  • Run playbooks against groups within the dynamic inventory

Prerequisites

  • Azure subscription: If you don't have an Azure subscription, create a free account before you begin.
  • Azure service principal: Create a service principal, making note of the following values: appId, displayName, password, and tenant.

Create Azure VMs

  1. Sign in to the Azure portal.

  2. Open Cloud Shell.

  3. Create an Azure resource group to hold the virtual machines for this tutorial.

    Important

    The Azure resource group you create in this step must have a name that is entirely lower-case. Otherwise, the generation of the dynamic inventory will fail.

    az group create --resource-group ansible-inventory-test-rg --location eastus
    
  4. Create two Linux virtual machines on Azure using one of the following techniques:

    • Ansible playbook - The article, Create a basic Linux virtual machine in Azure with Ansible and Create a basic Windows virtual machine in Azure with Ansible illustrates how to create a virtual machine from an Ansible playbook.

    • Azure CLI - Issue each of the following commands in the Cloud Shell to create the two virtual machines:

      az vm create \
      --resource-group ansible-inventory-test-rg \
      --name win-vm \
      --image MicrosoftWindowsServer:WindowsServer:2019-Datacenter:latest \
      --admin-username azureuser \
      --admin-password <password>
      
      az vm create \
      --resource-group ansible-inventory-test-rg \
      --name linux-vm \
      --image CentOS85Gen2 \
      --admin-username azureuser \
      --admin-password <password>
      

      Replace the <password> your password.

Add application role tags

Tags are used to organize and categorize Azure resources. Assigning the Azure VMs an application role allows you to use the tags as group names within the Azure dynamic inventory.

Run the following commands to update the VM tags:

az vm update \
--resource-group ansible-inventory-test-rg \
--name linux-vm \
--set tags.applicationRole='message-broker' 

az vm update \
--resource-group ansible-inventory-test-rg \
--name win-vm \
--set tags.applicationRole='web-server' 

Learn more about Azure tagging strategies at Define your tagging strategy.

Generate a dynamic inventory

Ansible provides an Azure dynamic-inventory plug-in.

The following steps walk you through using the plug-in:

  1. Create a dynamic inventory named myazure_rm.yml

    plugin: azure_rm
    include_vm_resource_groups:
      - ansible-inventory-test-rg
    auth_source: auto
    

    Key point:

    • Ansible uses the inventory file name and extension to identify which inventory plug-in to use. To use the Azure dynamic inventory plug-in, the file must end with azure_rm and have an extension of either yml or yaml.
  2. Run the following command to query the VMs within the resource group:

    ansible-inventory -i myazure_rm.yml --graph
    
  3. When you run the command, you see results similar to the following output:

    @all:
      |--@ungrouped:
      |  |--linux-vm_cdb4
      |  |--win-vm_3211
    

Both VMs belong to the ungrouped group, which is a child of the all group in the Ansible inventory.

Key point:

  • By default the Azure dynamic inventory plug-in returns globally unique names. That's the reason for the extra characters after the VM names. You can disable that by adding plain_host_names: yes to the dynamic inventory.

Find Azure VM hostvars

Run the following command to view all the hostvars:

ansible-inventory -i myazure_rm.yml --list
{
    "_meta": {
        "hostvars": {
            "linux-vm_cdb4": {
                "ansible_host": "52.188.118.79",
                "availability_zone": null,
                "computer_name": "linux-vm",
                "default_inventory_hostname": "linux-vm_cdb4",
                "id": "/subscriptions/<subscriptionid>/resourceGroups/ansible-inventory-test-rg/providers/Microsoft.Compute/virtualMachines/linux-vm",
                "image": {
                    "offer": "CentOS",
                    "publisher": "OpenLogic",
                    "sku": "7.7",
                    "version": "latest"
                },
                ...,
                "tags": {
                    "applicationRole": "message-broker"
                },
                ...
            },
            "win-vm_3211": {
                "ansible_host": "52.188.112.110",
                "availability_zone": null,
                "computer_name": "win-vm",
                "default_inventory_hostname": "win-vm_3211",
                "id": "/subscriptions/<subscriptionid>/resourceGroups/ansible-inventory-test-rg/providers/Microsoft.Compute/virtualMachines/win-vm",
                "image": {
                    "offer": "WindowsServer",
                    "publisher": "MicrosoftWindowsServer",
                    "sku": "2019-Datacenter",
                    "version": "latest"
                },
                ...
                "tags": {
                    "applicationRole": "web-server"
                },
                ...
            }
        }
    },
    ...
    }
}

By pulling information from Azure, the dynamic inventory populates the hostvars for each Azure VM. Those hostvars are then to determine the VM group memberships within the Ansible inventory.

Assign group membership with conditional_groups

Each conditional group is made of two parts. The name of the group and the condition for adding a member to the group.

Use the property image.offer to create conditional group membership for the linux-vm.

Open the myazure_rm.yml dynamic inventory and add the following conditional_group:

plugin: azure_rm
include_vm_resource_groups:
  - ansible-inventory-test-rg
auth_source: auto
conditional_groups:
  linux: "'CentOS' in image.offer"
  windows: "'WindowsServer' in image.offer"

Run the ansible-inventory with the --graph option:

ansible-inventory -i myazure_rm.yml --graph
@all:
  |--@linux:
  |  |--linux-vm_cdb4
  |--@ungrouped:
  |--@windows:
  |  |--win-vm_3211

From the output, you can see the VMs are no longer associated with the ungrouped group. Instead, each has been assigned to a new group created by the dynamic inventory.

Key point:

  • Conditional groups allow you to name specific groups within your inventory and populate them using hostvars.

Assign group membership with keyed_groups

Keyed groups assign group membership the same way conditional groups do, but when using a keyed group the group name is also dynamically populated.

Add the following keyed_group to the myazure_rm.yml dynamic inventory:

plugin: azure_rm
include_vm_resource_groups:
  - ansible-inventory-test-rg
auth_source: auto
conditional_groups:
  linux: "'CentOS' in image.offer"
  windows: "'WindowsServer' in image.offer"
keyed_groups:
 - key: tags.applicationRole

Run the ansible-inventory with the --graph option:

ansible-inventory -i myazure_rm.yml --graph
@all:
  |--@_message_broker:
  |  |--linux-vm_cdb4
  |--@_web_server:
  |  |--win-vm_3211
  |--@linux:
  |  |--linux-vm_cdb4
  |--@ungrouped:
  |--@windows:
  |  |--win-vm_3211

From the output, you'll see two more groups _message_broker and _web_server. By using a keyed group, the applicationRole tag populated group names and group memberships.

Key point:

  • By default, keyed groups include a separator. To remove the separator add separator: "" under the key property.

Run playbooks with group name patterns

Use the groups created by the dynamic inventory to target subgroups.

  1. Create a playbook called win_ping.yml with the following contents:

    ---
    - hosts: windows
      gather_facts: false
    
      vars_prompt:
        - name: username
          prompt: "Enter local username"
          private: false
        - name: password
          prompt: "Enter password"
    
      vars:
        ansible_user: "{{ username }}"
        ansible_password: "{{ password }}"
        ansible_connection: winrm
        ansible_winrm_transport: ntlm
        ansible_winrm_server_cert_validation: ignore
    
      tasks:
        - name: run win_ping
          win_ping:
    
  2. Run the win_ping.yml playbook.

    ansible-playbook win_ping.yml -i myazure_rm.yml
    

    When prompted, enter the username and password for the Azure Windows VM.

    Enter local username: azureuser
    Enter password:
    
    PLAY [windows] **************************************************************************************************************************************
    
    TASK [run win_ping] *********************************************************************************************************************************
    ok: [win-vm_3211]
    
    PLAY RECAP ******************************************************************************************************************************************
    win-vm_3211                : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
    

    Important

    If you get the error winrm or requests is not installed: No module named 'winrm', install pywinrm with the following command: pip install "pywinrm>=0.3.0"

  3. Create a second playbook named ping.yml with the following contents:

    ---
    - hosts: all
      gather_facts: false
    
      vars_prompt:
        - name: username
          prompt: "Enter ssh user"
        - name: password
          prompt: "Enter password for ssh user"
    
      vars:
        ansible_user: "{{ username }}"
        ansible_password: "{{ password }}"
        ansible_ssh_common_args: '-o StrictHostKeyChecking=no'
    
      tasks:
        - name: run ping
          ping:
    
  4. Run the ping.yml playbook.

    ansible-playbook ping.yml -i myazure_rm.yml
    

    When prompted, enter the username and password for the Azure Linux VM.

    Enter ssh username: azureuser
    Enter password for ssh user:
    
    PLAY [linux] *******************************************************************************************************
    
    TASK [run ping] ****************************************************************************************************
    ok: [linux-vm_cdb4]
    
    PLAY RECAP *********************************************************************************************************
    linux-vm_cdb4              : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0  
    

Clean up resources

  1. Run az group delete to delete the resource group. All resources within the resource group will be deleted.

    az group delete --name <resource_group>
    
  2. Verify that the resource group was deleted by using az group show.

    az group show --name <resource_group>
    

Next steps