Configure Ansible to use a Managed Identity with Azure Dynamic Inventory

Hello again, Chris Wallen here and in this post, I'm going to show you how to configure the Ansible Azure Dynamic Inventory plugin to use a managed identity. If you're not familiar with managed identities, see this overview of managed identities for Azure resources. Basically, they just provide a method of to Azure without the need to store credentials in code or on any local resource. Now, before we get into the technical details, let's go over the different sources of Azure credentials that Ansible can use to authenticate when using dynamic inventory:
When using dynamic inventory, you can use any of the following methods to provide your Azure credentials to Ansible:

Storing them in a plain-text credential file
Setting Environment variables that store the credentials
Using the currently logged in Azure CLI credentials
Using a managed identity
Obviously, the first option is the least secure as your credentials are stored in a flat file that could be read by anyone with access. The second option is more secure; however, it requires you to either create more to set the environment variables for the ansible user, permanently set them in the user profile, which requires you again to store them in plain text, or set the variables manually. The third option is also secure, but does not lend itself to as you have to manually sign in to the CLI each time you want to use dynamic inventory. This just leaves us with option 4. This is the most secure as you're not required to store credentials anywhere in code or on the local machine nor do you have to take any manual steps to authenticate. The only requirement is that your Ansible control server must be running in Azure. Once the managed identity is assigned, you can easily control the level of access to resources by using role-based access. Ok, now that we have that out of the way, let's talk about the prerequisites.

The commands in this guide assume the use of Azure CLI in Azure Cloud Shell. You can also use a local CLI; however, if you're not running on , you'll need to convert the commands for Powershell or command prompt.
An Ansible control server (v2.8 at minimum) deployed in Azure. For installation steps, see Ansible's Microsoft Azure Guide
At least one VM in Azure ( or Windows). You'll also need to have it configured for communication with Ansible (ssh or winrm depending on OS)
Now, on to the dynamic inventory configuration. First, we'll need a managed identity to assign to our Ansible control server.
Create a managed identity
You can use either a system-assigned or user-assigned identity. To decide which type is best for you, see the differences between a system-assigned and user-assigned managed identity. Regardless of which type you choose, we'll need to first create the identity using Azure CLI in Azure Cloud Shell.
NOTE: If you already have an identity created, skip to the section titled “Assign RBAC rights to the managed identity”.
User-Assigned Identity
To create a new user-assigned identity, run the command below, replacing RG-NAME with the name of the resource group for the identity, and IDENTITY-NAME with the name of the identity.
az identity create -g RG-NAME -n IDENTITY-NAME
System-Assigned Identity
For a system-assigned identity, run the following command, where RG-NAME is the name of the resource group and VMNAME is the name of the Ansible control server
az vm identity assign -g RG -n VMNAME
Assign RBAC rights to the managed identity
Now that we have the identity created, we need to assign it rights to the Azure resources we want to inventory. I chose to use the principle of least privilege and only mine Reader rights on the resource group that I'll be using for dynamic inventory. If you want to use multiple resource groups for your inventory, you'll need to follow this procedure for each one.
User-Assigned Identity
To assign Reader for a resource group to a user-assigned identity, run the below commands in the Cloud Shell. To run the commands, you'll need to replace IDENTITY-NAME with the name of the managed identity you created in the previous step and replace the scope with the resource id of the resource group to assign the rights to (found under properties blade of the resource group)
spID=$(az identity show -n IDENTITY-NAME -g RG –query ‘[principalId]' -o tsv)
az role assignment create –assignee $spID –role ‘Reader' –scope /subscriptions/SUBID/resourceGroups/RG-NAME
az role assignment create –assignee $spID –role ‘Reader' –scope /subscriptions/ fddda745-b9e7-43e4-9a2a-45fa890210ed /resourceGroups/myDemoRG
System-Assigned Identity
To assign Reader rights for a resource group to a system-assigned identity, run the following commands in the Cloud Shelll. To run the commands, replace ANSIBLE-VM with the name of the Ansible VM as it appears in the portal and replace the scope with the resource id of the resource group to assign the rights to (found under properties blade of the resource group
spID=$(az resource list -n VM-NAME –query [*].identity.principalId –out tsv)
az role assignment create –assignee $spID –role ‘Reader' –scope /subscriptions/SUBID/resourceGroups/RG-NAME
az role assignment create –assignee $spID –role ‘Reader' –scope /subscriptions/ fddda745-b9e7-43e4-9a2a-45fa890210ed /resourceGroups/myDemoRG
Configure dynamic inventory
With Ansible 2.8 and later, dynamic inventory is handled through the azure_rm plugin. To configure the VMs for dynamic inventory, I used this tutorial. I'll summarize the steps below:
Configure Azure Resources
Add a tag to the you want to inventory. To create the tag, run the below command. Make sure to replace RG-NAME and VM-NAME with values appropriate for your environment.
az vm update
    –resource-group RG-NAME
    –name VM-NAME
    –set tags.mytag=demo
Create the dynamic inventory file
Use the steps below to setup the Ansible directory and create the inventory file

Open an SSH session to your Ansible control server
Run the following command to create a directory structure for Ansible under your users home directory
mkdir -p ~/ansible/inventory
cd ~/ansible/inventory

Use the editor of your choice to create the inventory file. The one requirement is that the file name must end in aure_rm and can have either .yml or .yaml extension. For example, I named my file: demo_azure_rm.yaml. Once created, add the following contents to the file, being sure to replace RG-NAME with the name of the resource group that contains the VMs you want to manage.
plugin: azure_rm
auth_source: msi
- prefix: mytestgroup
  key: tags.mytag | default(‘none')
- powerstate != 'running'
Before moving on to the next section, let's take a second and go over the inventory file to make sure all of the parameters are clear.

The auth_source parameter specifies the type of to use. With the MSI value, Ansible will use the assign
ed managed identity for . For other available auth types, see Ansible's azure_rm guide
The keyed_groups section will create a group based on the specified parameter. This is where the tag we created earlier comes into play. The prefix parameter defines the group name that we'll use for the hosts that match the condition specified for the key parameter. The group name will be a combination of the string you specify + _key. The key parameter is looking for all VMs in the resource group that have the mytag tag. All of these VMs will be grouped by the value of the mytag tag. For this example, a host group named mytestgroup_demo will be created as that was the value of the tag I created. 
The exclude_host_filters section will exclude any hosts that match the condition. In this case, any hosts not in a running state will be excluded. This section can be removed if you're not concerned with the VMs status.
Test the configuration
Now that we have everything setup, it's time to test our configuration. In order to do so, let's run an ad-hoc command against the inventory file:
ansible mytestgroup_demo -m ping -i ~/ansible/inventory/demo_azure_rm.yaml
You should see output similar to the following:

NOTE: The ping module will only work against VMs. If testing against Windows, use the win_ping module.
And that's it. You now have Ansible configured to use a managed identity with Azure Dynamic Inventory. In my next post, I'll take this a step further and show how you can use dynamic inventory to apply to Windows VMs in Azure.


This article was originally published by Microsoft's Azure Blog. You can find the original article here.