I'm using ansible to provision a particular service, and before I can interact with it I must first generate an API key. But I can't predefine that key in my playbook (as a secret) - it is generated by the server, returned to me once, and will never be exposed again (the typical scnenario for API keys).
I could ask the server to generate a new API key every time I run that playbook, or tags in that playbook, but that is very slow. Ideally I should save that API key locally and reuse it.
My approach has been to simply write it to a local file in my user directory (~/.ansible/custom/api_key.txt
) so it has some protection. That works but feels kind of dirty.
Does ansible have an official / robust way to handle this scenario?
This is kind of a broad question. There are a variety of ways of handling secrets in your playbooks; this article describes several options, and there are a variety of articles online that cover similar topics.
A simple option might be to encrypt the API key to a GPG public key for which the private key is only available when you're logged in and able to provide the passphrase.
Here's a simple (i.e. not particularly robust) example:
- hosts: localhost
gather_facts: false
tasks:
# Check if the file in which we cache the API key exists.
# If not, fetch the API key from the API and store it in
# a GPG-encrypted file.
- when: apikey_file is not file
block:
# This is just a dummy task to give us a string; you would of
# course replace this with the logic to acquire an API key.
- name: Get key from API
command: echo secret.key
register: apikey
# Encrypt the key using the public key for the identity
# stored in apikey_gpg_id.
- name: Write API key to file
command: >-
gpg -o "{{ apikey_file }}" -e -r "{{ apikey_gpg_id }}"
args:
stdin: "{{ apikey.stdout }}"
- hosts: localhost
gather_facts: false
tasks:
# Decrypt the API key file to stdout. This requires us to type in
# the passphrase (which may be cached for some amount of time in your
# GPG agent).
- name: Read API key from file
command: >-
gpg -d "{{ apikey_file }}"
register: apikey
# Show what we got from the previous task.
- debug:
var: apikey.stdout
This presumes that the variables apikey_file
and apikey_gpg_id
are defined in advance -- I've put them in group_vars/all.yaml
, but they could also be defined in your inventory or elsewhere depending on how your project is structured.