Since at least July 2013 (the initial commit in my current Salt configuration repository at home) I have been using SaltStack to orchestrate and configure my systems, including VMs, at home. In the last few years Ansible has grown in popularity and I have recently been looking closely at it due to its integration with Azure and Microsoft include Ansible in their Azure documentation and Cloud Shell Platform.

Microsoft’s example uses a dedicated Virtual Machine to run Ansible, however this is not desirable for a number of reasons including an unnecessary additional machine to manage and the costs associated with having another virtual machine. It is, with a little bit of effort, possible to run Ansible directly through the Azure DevOps service as Russ McKendrick described. What follows is my version, with Ansible’s dynamic inventory module, based upon the work he started.

The pipeline

After a bit of tweaking, I came up with this working pipeline configuration file:

# Do not automatically run this pipeline
trigger:
- none

steps:
  - task: UsePythonVersion@0
    displayName: 'Install Python'
    inputs:
      versionSpec: '3.6'
  - task: AzureCLI@2
    displayName: 'Azure CLI'
    inputs:
      # Pass SUBSCRIPTION_NAME as an argument to the playbook run - everything else it will figure out
      azureSubscription: '$(SUBSCRIPTION_NAME)'
      addSpnToEnvironment: true
      scriptType: 'bash'
      scriptLocation: 'inlineScript'
      inlineScript: |
        echo "##vso[task.setvariable variable=ARM_SUBSCRIPTION_ID]$(az account show --query="id" -o tsv)"
        echo "##vso[task.setvariable variable=ARM_CLIENT_ID]${servicePrincipalId}"
        echo "##vso[task.setvariable variable=ARM_CLIENT_SECRET]${servicePrincipalKey}"
        echo "##vso[task.setvariable variable=ARM_TENANT_ID]${tenantId}"
  - script: pip install ansible
    displayName: 'Install Ansible'
  - script: pip install -r https://raw.githubusercontent.com/ansible-collections/azure/dev/requirements-azure.txt
    displayName: 'Install modules needed for Azure'
  - script: ansible-galaxy collection install azure.azcollection
    displayName: 'Install Ansible Azure Collection'
  - script: ansible-playbook -i Ansible/turned-on-inventory.azure_rm.yml Ansible/site.yml
    displayName: 'Run Ansible Playbook'
    env:
      AZURE_CLIENT_ID: $(ARM_CLIENT_ID)
      AZURE_SECRET: $(ARM_CLIENT_SECRET)
      AZURE_TENANT: $(ARM_TENANT_ID)
      AZURE_SUBSCRIPTION_ID: $(ARM_SUBSCRIPTION_ID)

The inventory

This tells Ansible which plugin to use and adds the resource group properties from Azure as a mechanism to target machines with the prefix ‘rg’ (e.g. ‘rg-my-resource-group’). The file was saved as ‘turned-on-inventory.azure_rm.yml’ (by default VMs that are not running or are not provisioned are excluded - see the Ansible documentation for azure_rm inventory) in the ‘Ansible’ subdirectory.

# Use the azure_rm plugin
plugin: azure.azcollection.azure_rm

# Allow us to target resource groups with 'rg-[...]'
keyed_groups:
  - key: resource-group
    prefix: rg

The Ansible playbook

I kept this really simple, and just used the example from Microsoft’s page which shows the running state for all of the VMs in Azure. The file was saved as ‘site.yml’ in the ‘Ansible’ subdirectory of the repository:

- name: Test the inventory script
  hosts: azure
  connection: local
  gather_facts: no
  tasks:
    - debug: msg="{{ inventory_hostname }} has powerstate {{ powerstate }}"

Future work

I want to explore if I can do what we currently do with Azure DevOps with GitHub actions instead as well as migrate existing laborious, monolithic and/or manual processes to more automated, encapsulated and streamlined tools.