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 :).
This post helped me out immensely! Only place on the internet that details it well! One question however, what about the scenario where multiple secrets are stored within the same vault? What is a clean way to ensure the injector configuration
“{{ azurekv_secret }}” maps to the correct look up?
for example
credential1 in azure key vault
credential2 in azure key vault
within the same playbook/template.
Thanks very much 🙂
EDIT: Take a look at https://www.tinfoilcipher.co.uk/2021/05/17/ansible-tower-and-azure-keyvault-managing-multiple-secrets/
Even i do have the same scenario, to lookup for two diff secrets from azure key vault and use it in same playbook. can you mail me if you are doing the similar?
I too have to use two secrets which are in same vault of azure. i have created something in the playbook which i can lookup for single secret. But i’m unable to add more than one. Any idea?
Yes I see the issue, let me stand up my tower instance and get a working example added to the article.
EDIT: Take a look at https://www.tinfoilcipher.co.uk/2021/05/17/ansible-tower-and-azure-keyvault-managing-multiple-secrets/
It worked.. thanks Andy