The string that pretended to be an ansible variable.
Its not unusual in Ansible to import a playbook inside another playbook. This allows for modularization and reutilization of the source code. Its also not unusual to pass variables to that new playbook, so it can use them. This was the initial setup I had to debug not long ago: A flag variable, marking if a folder exists or not, this information necesary to bifurcate a whole series of operations in the next playbook.
The theory: Passing a variable to another playbook to reference it
The imported playbook didn´t make a correct use of the variable and broke the whole process. At a glance, no issues. Funny thing was that it didn´t threw the undefined variable
error, it went on without issues, behaving like if the variable was always true
.
This is the -adapted- code in question. I´m sure many will spot the problem inmediately:
---
- hosts: group_a
tasks:
- name: Register folder status
ansible.windows.win_stat:
path: "{{ predeploy_dir }}"
register: predeploy
- import_playbook: install-artifacts.yml
vars:
predeploy_done: predeploy.stat.exists
When in doubt, debug print, debug print and debug print.
As I said, at a first glance I didn´t catch any errors (my rookie mistake). So I asked myself a question that I, at least, find very useful when facing any source code: “Is it really doing what I think its doing?”. The way that question materialized here is by debugging the variable values. In Ansible we have ansible.builtin.debug
for that. So lets get to it. On the importet playbook I added:
- name: Debug variable
ansible.builtin.debug:
var: predeploy_done
Did a test run and the debug printed the variable´s value.
ok: [node-a] => {
"predeploy_done": "predeploy.stat.exists"
}
That did not look like a boolean at all. The value contained in predeploy.stat.exists
was not being assigned to the variable, the string "predeploy.stat.exists"
was. Of course, downstream in the imported playbook this was evaluated in the condition, as the boolean true
, because Ansible is based on Python, and in Python any non-empty string is true
.
The error I didn´t manage to see in the first place was that bad variable assignment. But now everything made sense, and in my experience that is the essential step to fixing anything.
The fix, and being a bit more practical.
I´ve already catched the problem: The variable was not being assigned correctly. Fixing that was as easy as predeploy_done: '{{ predeploy.stat.exists }}'
. However, I decided to made the value available to the remaining playbooks during the execution (contrary to what I tend to do, this time I didn´t stop to think what would be more efficient. I needed a quick fix and as practical as possible). Setting the value for a variable in the localhost for the whole execution is something Ansible allows us to do with set_fact
. In the new code there´s no need to worry about importing predeploy_done
on the importing playbook (once set with set_fact
its available from that moment onwards for all the playbooks in that host, for the remaining execution time)
---
- hosts: group_a
tasks:
- name: Register folder status
ansible.windows.win_stat:
path: "{{ predeploy_dir }}"
register: predeploy
- name: Set global variable predeploy_done
ansible.builtin.set_fact:
predeploy_done: "{{ predeploy.stat.exists }}
- import_playbook: install-artifacts.yml