I have an Ansible role that deploys microservices, of whom I have a list of. The microservices are called foo
, bar
and baz
.
I also have a list of stages, called DEV
, QA
and PROD
where the microservices get deployed to.
Of course, every microservice needs to connect to a database using credentials. These credentials are different for every microservice in every stage.
This means I have nine different password-variables stored in an Ansible-vault encrypted file. I named them {{ stage }}_{{ microservice }}_password
, e.g. prod_foo_password
.
The aforementioned role contains a task that creates the credentials for the database-connection and stores them in a secret (to be later used by the microservice). How do I pass the correct password for the stage-microservice-combination to this credential-creation task?
I could just write nine tasks, one for every combination of microservice and stage. That’s probably not even a bad idea to keep the code simple and easy to understand. That will look like this (illustrated with debug-tasks and shortened):
- hosts: localhost
vars:
# these are the secrets that are in my vault-file
foo_QA_password: foo
bar_QA_password: bar
baz_QA_password: baz
foo_DEV_password: foo
bar_DEV_password: bar
baz_DEV_password: baz
tasks:
- name: "create secret for foo in stage DEV"
debug:
msg: "{{ foo_DEV_password }}"
- name: "create secret for bar in stage QA"
debug:
msg: "{{ bar_QA_password }}"
....
However, in my case, it’s not practical as the microservices and stages will change over time, meaning I’d have to add or delete tasks all the time.
What I did instead was to dynamically construct a variable from other variables, additionally using a loop:
- hosts: localhost
vars_prompt:
- name: "stage"
prompt: "Stage of the vhost (DEV, QA, PROD)"
private: no
vars:
# these are the secrets that are in my vault-file
foo_QA_password: foo
bar_QA_password: bar
baz_QA_password: baz
# list of microservices
microservices:
- "foo"
- "bar"
- "baz"
tasks:
- name: "create secret for {{ item }} in stage {{ stage }}"
debug:
msg: "{{ lookup('vars', item + '_' + stage + '_password') }}"
loop: "{{ microservices }}"
The interesting part is this:
data: "{{ lookup('vars', item + '_' + stage + '_password') }}"
loop: "{{ microservices }}"
This means that I first loop over all microservices (stored in the item
-variable). I then create a lookup. In this lookup I look for vars
that contain my item
following the _
-string, then the content of the variable stage
(notice the missing curly braces here), finally the _password
-string. The result is lookup('vars', item + '_' + stage + '_password')
- this in turn gets interpolated to the actual password from my vault-file!
The magic here is done by the vars-lookup plugin. This lookup retrieves the value of an Ansible variable. And this variable can be made of other variables or strings. Definitely an advanced feature of Ansible, but still good to know that it exists.