Azure Key Vault - Integrating With Ansible Tower

Recently we looked at integrating Ansible Tower with Hashicorp Vault, but I thought it would be worth taking a look at another popular Secrets management system, Azure Key Vault. Whilst the solution isn’t exactly the same using Azure Key Vault and Tower was my first time trying to integrate Ansible with a centralised Secrets repository, so let’s take a look at how to achieve the integration as it’s not very well documented and the specifics (like some of the handiest functions of Tower) are nice and buried inside the internal Red Hat knowledge base.

Ansible Tower Credentials – Native Integration

Ahead of time, I’ve got my Key Vault in Azure named tinfoil-keyvault and have granted appropriate permissions for an existing Service Principal to access the Key Vault and have pre-populated it with some secrets:

Ansible Tower has a native Credential type for Azure Key Vault, if we add a new Credential in Tower we can select Microsoft Azure Key Vault, this is the integration with a the Key Vault API:

Setting up the Credential further, we will need to specify:

  • The URL of the Azure Key Vault instance.
  • The Client ID and Client Secret of the Service Principal which will be used to access the instance.
  • The Tenant ID of the Azure tenancy

Key Vault, But No Secrets?

So now we have an integration with our Key Vault instance, but we don’t actually have a means to look up the individual secrets to Tower, this could be problematic.

Within Tower, we will define a custom Credential Type to map custom metadata from the Key Vault integration and then inject this data in to playbooks when required.

Within Tower, we can browse to Credential Types and add a new YAML structure to define our new Credential, this will be used as a template for any credentials that we look up from our Key Vault:

Creating this Custom Secret will create a new Credential Type named Microsoft Azure Key Vault Secret.

The structures are broken down in detail below:

--# INPUT CONFIGURATION

--# The fields define the GUI fields which will be presented, only one is provided
--# and it is mandatory, a string named secret which will hold the Secret data initially
--# it's unique id is "secret" and it's type is "secret" meaning it will be obscured on
--# input, we have also defined it as "required" meaning it has to be entered
fields:
  - id: secret 
    type: string
    label: Secret
    secret: true
required:
  - secret

--# INJECTOR CONFIGURATION

--# Here the "secret" value is converted to a new variable named
--# azurekv_secret, this value can be read in to playbooks
extra_vars:
  azurekv_secret: '{{ secret }}'

Looking Up an Individual Secret

Now that we have this means of looking up individual secrets, we still need to load them in to Tower so that they can be assigned to Templates. In this example, I’m going to look up a Secret named secret1 and obtain it’s secret data held in a key named password for use in Tower Templates:

When attempting to create the new Credential, the option is present to enter the secret data manually, but we want to look up from our Key Vault instance which we have already authenticated with, clicking on the “search” icon next to the Secret field will allow us to perform a lookup action against our Key Vault:

The lookup will first ask which external system we wish to lookup from (we only have one):

On the next page we need to define metadata for this lookup. This is where we define the data for our specific Secret, defining the name and optionally; the version:

The Test button can be used to validate that a successful lookup has been made and the OK button will save the new _Credential:

As we see, the full credential is now created with nested lookup data:

Using the Credential in Ansible

Now that a Credential has been passed in to Tower, it can be assigned to any Template in the same manner as any other Credential and used in a Playbook. In the below example, I’m using one of the great community modules for Netbox from @FragmentedPacket and @amb1s1 to create a new Netbox Device:

---
- name: "Netbox Demo"
  connection: local
  hosts: localhost
  gather_facts: False

  tasks:
    - name: Create Netbox Device
      netbox_device:
        netbox_url: https://mc-apps.madcaplaughs.co.uk
        netbox_token: "{{ vaultkv_secret }}"
        data:
          name: Test Switch
          device_type: TLSG105S
          device_role: Managed Switch
          site: madcaplaughs
        state: present
...

The variable being called on line 11 can be read (provided that the Credential secret1 has been assigned to the Template calling this Playbook) as it will be passed from the Injector Configuration of the Custom Credential Type we created earlier.

Using this method we can still store our data within Tower’s own Fernet encrypted database, however the secrets can still remain managed outside of Ansible in Azure Key Vault.

Looking Up Multiple Secrets

I was eventually asked about the scenario of how to look up multiple Secrets and assign them to the same Credential, see the later article here for an answer on that scenario :).

Written on April 2, 2020