Reputation: 7025
I'm a bit puzzled about Ansible behaviour: it looks like when I request a single key's value, it evaluates all values in the dictionary.
Here's my case. I have a dictionary with two keys: dev
and prod
. Each key's value is defined via a specific Jinja2 expression that involves decryption using AWS KMS. Roughly it looks like this:
mydict:
dev: '{{ "dev-ciphertext" | kms_decrypt }}'
prod: '{{ "prod-ciphertext" | kms_decrypt }}'
kms_decrypt
is a custom filter that decrypts the ciphertext. Since dev
and prod
environments are separated, and the playbook runs within an encryption context valid for only one of these environments, only one expression can be evaluated at a time. Attempt to retrieve a value from the other key will fail.
So let's say the playbook runs in the context of dev
environment, and when I evaluate mydict['dev']
, I expect it to return decrypted dev-ciphertext
. However, what I get in fact is failure on decryption of prod-ciphertext
, because the encryption context does not match.
I can illustrate the same behaviour in a simpler example. Instead of recreating the decryption mechanism, I just defined one of the dictionary values via an undefined variable:
- hosts: localhost
become: no
vars:
dev_value: '123'
mydict:
dev: '{{ dev_value }}'
prod: '{{ prod_value }}'
tasks:
- debug:
msg: "{{ mydict['dev'] }}"
Regardless of the fact that mydict['prod']
is never explicitly queried, I still get an error indicating that it can't be evaluated:
TASK [debug] *********************************************************
fatal: [localhost]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'prod_value' is undefined\n\nThe error appears to have been in 'ansible/test.yml': line 9, column 7, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n tasks:\n - debug:\n ^ here\n"}
I realize that there are a lot of ways to work around this problem, but can anyone explain why does the whole dictionary values get evaluated when only a single key is queried? It doesn't seem to make sense to me - at least from performance point of view.
Upvotes: 1
Views: 344
Reputation: 33231
Jinja templates are rendered in all circumstances -- there is no such thing as "lazy jinja," only lazy tasks via the when:
or similar guards
So if you don't already have my_dict["dev"]
scattered all over your code, then I would suggest using a reasonable name like my_env
or something, and only declare the one value that is meaningful for the active environment:
- hosts: localhost
become: no
vars:
dev_value: '123'
tasks:
- set_fact:
my_env: '{{ "prod-ciphertext" | kms_decrypt }}'
when: some_environment_variable == "prod"
- set_fact:
my_env: '{{ "dev-ciphertext" | kms_decrypt }}'
when: some_environment_variable == "dev"
Otherwise, you can guard the expressions -- which for clarity are always going to be evaluated -- to return defaults if they are not applicable:
- hosts: localhost
become: no
vars:
dev_value: '123'
my_dict:
prod: '{{ ("prod-ciphertext" | kms_decrypt)
if the_magic_env == "prod" else {} }}'
dev: '{{ ("dev-ciphertext" | kms_decrypt)
if the_magic_env == "dev" else {} }}'
tasks:
- debug:
msg: "{{ mydict['dev'] }}"
Upvotes: 1