Ansible (3)

Date: 2024/02/20 (initial publish), 2024/03/02 (last update)

Source: en/note-00067.md

Previous Post Top Next Post

TOC

Here is a series of memos of me trying to use ansible on Debian 12 (bookworm).

FYI: Test code github repo

Recap of my trials of playbooks

Up to here, I tried simple playbooks in which a playbook runs in order from top to bottom.

Let me read over again “Playbook execution”:

So far, it was simple.

Using Variables in Ansible

Then I read “Using Variables”. This feature of “Ansible uses variables to manage differences between systems.” is a very attractive one. But when I tried variables, I got confused a bit until I found “Ansible loads every possible variable it finds, and then chooses the variable to apply based on variable precedence”.

Variable precedence

Here is the order of precedence from least to greatest for notable ones:

I noticed these variable settings are somewhat like Makefile. Unlike normal shell scripts, vars: ... definition after task: ... are effective and values may be updated as tasks are processed.

Strategy to use variables

I can understand the rationale behind making “extra vars” to have a powerful precedence position. It’s handy for debugging.

But, I can’t use such “extra vars” variable names casually within the play and try to rewrite their effective values with other variables. So the approach of using corresponding but different internal variable names is the only reasonable alternative. Then I can handle default values for the undefined cases and variable type issues gracefully.

I put such variable name conversion code from externally exposed to change Ansible behavior to corresponding internal one in the low priority “role default”. It also takes care variable type issues.

Testing variables in action

Let me setup variable test code using following modules:

roles/base/defaults/main.yml:

---
# prefix internal variables with underscore
#   -- let -e to set external variables without underscore
# ensure internal variables to be defined (and boolean for override)
#
_var1: "{{ var1 | default('1') }}"
_var2: "{{ var2 | default('2') }}"

#_varTF: True
_varTF: "{{ _var1 == _var2 }}"

_var1_override:  "{{ var1_override | default(false) | bool }}" 
_var2_override:  "{{ var2_override | default(false) | bool }}" 

roles/base/vars/main.yml:

---
# vars file

_var3: ""

Then tested these against roles/base/tasks/main.yml and played with it from the command line. Many repeated long lines are for the variable value tracing.

roles/base/tasks/main.yml:

---

- name: Debug external variables with msg
  ansible.builtin.debug:
    msg: "var1_override = '{{ var1_override | default('***UNDEFIINED***') }}' var2_override = '{{ var2_override | default('***UNDEFIINED***') }}'"

- name: Debug internal variables with msg
  ansible.builtin.debug:
    msg: "_var1 = '{{ _var1 }}' _var2 = '{{ _var2 }}' _var3 = '{{ _var3 }}' _varTF = '{{ _varTF }}' _var1_override = '{{ _var1_override }}' _var2_override = '{{ _var2_override }}'"

- name: set var1 to "111" (if _var1_override is True)
  ansible.builtin.set_fact:
    _var1: "111"
  when: _var1_override

- name: Debug internal variables with msg
  ansible.builtin.debug:
    msg: "_var1 = '{{ _var1 }}' _var2 = '{{ _var2 }}' _var3 = '{{ _var3 }}' _varTF = '{{ _varTF }}' _var1_override = '{{ _var1_override }}' _var2_override = '{{ _var2_override }}'"

- name: set var2 to "222" (if _var2_override is True)
  ansible.builtin.set_fact:
    _var2: "222"
  when: _var2_override

- name: Debug internal variables with msg
  ansible.builtin.debug:
    msg: "_var1 = '{{ _var1 }}' _var2 = '{{ _var2 }}' _var3 = '{{ _var3 }}' _varTF = '{{ _varTF }}' _var1_override = '{{ _var1_override }}' _var2_override = '{{ _var2_override }}'"

- name: set _var3 (_var1+_var2)
  ansible.builtin.set_fact:
    _var3: "{{ _var1 + _var2 }}"

- name: Debug internal variables with msg
  ansible.builtin.debug:
    msg: "_var1 = '{{ _var1 }}' _var2 = '{{ _var2 }}' _var3 = '{{ _var3 }}' _varTF = '{{ _varTF }}' _var1_override = '{{ _var1_override }}' _var2_override = '{{ _var2_override }}'"

- name: set _var3 to "zzzzz" with var1 == var2
  ansible.builtin.set_fact:
    _var3: "zzzzz"
  when: _varTF

- name: Debug internal variables with msg
  ansible.builtin.debug:
    msg: "_var1 = '{{ _var1 }}' _var2 = '{{ _var2 }}' _var3 = '{{ _var3 }}' _varTF = '{{ _varTF }}' _var1_override = '{{ _var1_override }}' _var2_override = '{{ _var2_override }}'"

- name: Debug result of _var1 == _var2
  ansible.builtin.debug:
    var: _var1 == _var2

Using these, I tested as:

 $ ansible-playbook pb_main.yml -e "var1=0 var2=0 var1_override=1 var2_override=0"
...

YAML or JSON is too much to type in the command line. The -e option with easier to type key=value produces value in string. The application of |bool in Ansible code as above ensures the value to be boolean .

Now I understand why I see many when: some_vaiable |bool in Ansible code examples. See Ansible issue #17193.

YAML block and linebreak

I knew “|” and “>” are for keeping the linebreak or converting the linebreak to the space from Ansible’s YAML syntax page.

I see many playbooks use “|+” instead and wasn’t sure what it means until I found YAML spec on block style defining “+” to retain linebreaks for blank lines following the string block and “-” to strip the linebreak at the end of string block.

For any “|”, “|+”, “|-” cases, linebreaks inside of the string block are retained.

YAML and shell “Here Documents”

With Ansible (2.14.3) with jinja (3.1.2), many previously raised issues (#12856, #32800) seem to be resolved now.

So no more extra space added to require <<' END' etc.

I have tested this with a single “cat``" command:

Any time “free_form” is used, single leading spaces are somehow dropped.

ansible.builtin.shell: and ansible.builtin.raw: don’t require command name to be specified as the full path from / on the target system.

ansible.builtin.script: requires command name to be specified as the full path from / on the target system.

In short, ansible.builtin.shell: with cmd:| in the following indented line to start indented shell code block is the best option for me.

If creating a file is the objective, following builtin should also be considered.

Debug hints

I found interesting post on redit.

TBH, “Ansible Documentation” is too big to read though whenever I wonder. Here are links to the portion I keep coming back.

Previous Post Top Next Post