Write /etc/hosts via ansible using variables inside magical variables [duplicate] - ansible

I have an Ansible playbook like the one below, I want use nested variable like this:
msg={{{{Component}}.community_release_num}}, but when I run playbook:
ansible-playbook vartest.yml -e 'version=version_402', it not work
[es#vpn-server nested-var]$ tree
.
├── vars
│   ├── horizon.yml
│   └── version_402.yml
└── vartest.yml
1 directory, 3 files
[es#vpn-server nested-var]$ cat vartest.yml
---
- name: test
hosts: localhost
vars_files:
- vars/{{version}}.yml
tasks:
- debug: msg={{{{Component}}.community_release_num}}
- debug: msg={{{{Component}}.release_num}}
[es#vpn-server nested-var]$ cat vars/horizon.yml
Component: horizon
[es#vpn-server nested-var]$ cat vars/version_402.yml
- horizon:
community_release_num: '9.0.1'
release_num: '4.0.2'
[es#vpn-server nested-var]$
error messages
[es#vpn-server nested-var]$ ansible-playbook vartest.yml -e 'version=version_402'
/usr/lib64/python2.6/site-packages/cryptography/__init__.py:25: DeprecationWarning: Python 2.6 is no longer supported by the Python core team, please upgrade your Python.
DeprecationWarning
PLAY [test] *******************************************************************************************************
/usr/lib64/python2.6/site-packages/Crypto/Util/number.py:57: PowmInsecureWarning: Not using mpz_powm_sec. You should rebuild using libgmp >= 5 to avoid timing attack vulnerability.
_warn("Not using mpz_powm_sec. You should rebuild using libgmp >= 5 to avoid timing attack vulnerability.", PowmInsecureWarning)
TASK [debug] ******************************************************************************************************
fatal: [localhost]: FAILED! => {"failed": true, "msg": "template error while templating string: expected token 'colon', got '}'. String: {{{{Component}}.community_release_num}}"}
to retry, use: --limit #/data/wangqian/artemis-code-test/artemis/ansible/update/nested-var/vartest.retry
PLAY RECAP ********************************************************************************************************
localhost : ok=0 changed=0 unreachable=0 failed=1
Can Ansible use nested variable, if yes, how to use it?

Per Ansible FAQ:
Another rule is ‘moustaches don’t stack’. We often see this:
{{ somevar_{{other_var}} }}
The above DOES NOT WORK, if you need to use a dynamic variable use the
hostvars or vars dictionary as appropriate:
{{ hostvars[inventory_hostname]['somevar_' + other_var] }}
So in your case:
- debug: msg={{hostvars[inventory_hostname][Component].community_release_num}}
Or:
- debug: msg={{vars[Component].community_release_num}}
Or (since Ansible 2.5):
- debug: msg={{(lookup('vars', Component)).community_release_num}}

Run this command:
ansible-playbook 1_lambda-facts.yml -e "func_name=mylambdaFunctionName"
- name: get lamda Info
hosts: localhost
connection: local
become: yes
gather_facts: true
tasks:
- name: List all for a specific function
lambda_facts:
query: all
function_name: "{{func_name}}"
- name: debug info
debug:
msg: "{{lambda_facts}}"
msg: "variable name is: {{vars['func_name']}}"
msg: "{{lambda_facts['function'][vars['func_name']]['function_arn']}}"

Related

ansible nested variable lookup refactoring

I'm using this kind of ansible lookup, in order to load the content of a file into a variable :
- name: Prepare ignition for worker nodes
set_fact:
custom_attr: "{{ lookup('file', './files/ignition/{{ oc_cluster_name }}/worker.ign') | b64encode }}"
when: item.name.startswith('worker')
I know that we should avoid using nested variables (moustaches don't stack, right ?). This code is working indeed, but I'm not sure it's the correct way to write this.
Is there another way to do it ? I used to write in two separate "set_fact" blocks, which works as well, but it's not better (using temporary vars) :
- name: Prepare ignition for worker nodes
block:
- name: locate file for worker node
set_fact:
ignition_file: "./files/ignition/{{ oc_cluster_name }}/worker.ign"
- name: load file into fact for worker node
set_fact:
custom_attr: "{{ lookup('file', ignition_file) | b64encode }}"
when: item.name.startswith('worker')
What do you think ?
I'm trying to write nice code with best practices : using no temporary variable and respecting the way to nest interpolation of variables
Moustaches shouldn't be stacked because it's not necessary to do so. You're already in a Jinja expression so you just access variables by name without wrapping them in more delimiters.
- name: Prepare ignition for worker nodes
set_fact:
# Relative paths are looked for in `files/` first, so there's no need to specify it
custom_attr: "{{ lookup('file', 'ignition/' ~ oc_cluster_name ~ '/worker.ign') | b64encode }}"
when: item.name.startswith('worker')
You can also use a temporary variable without a separate set_fact, which can be helpful for breaking up complex expressions:
- name: Prepare ignition for worker nodes
set_fact:
custom_attr: "{{ lookup('file', ignition_file) | b64encode }}"
vars:
ignition_file: ignition/{{ oc_cluster_name }}/worker.ign
when: item.name.startswith('worker')
Q: "Write nice code."
A: Put the declarations into the vars. For example, into the group_vars/all
shell> tree .
.
├── ansible.cfg
├── files
│   └── ignition
│   └── cluster1
│   └── worker.ign
├── group_vars
│   └── all
├── hosts
└── pb.yml
4 directories, 5 files
shell> cat ansible.cfg
[defaults]
gathering = explicit
inventory = $PWD/hosts
remote_tmp = ~/.ansible/tmp
retry_files_enabled = false
stdout_callback = yaml
shell> cat files/ignition/cluster1/worker.ign
test
shell> cat group_vars/all
oc_cluster_name: cluster1
ignition_file: "./files/ignition/{{ oc_cluster_name }}/worker.ign"
custom_attr: "{{ lookup('file', ignition_file)|b64encode }}"
shell> cat hosts
localhost
shell> cat pb.yml
- hosts: localhost
tasks:
- debug:
var: custom_attr|b64decode
shell> ansible-playbook pb.yml
PLAY [localhost] *****************************************************************************
TASK [debug] *********************************************************************************
ok: [localhost] =>
custom_attr|b64decode: test
PLAY RECAP ***********************************************************************************
localhost: ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

Force Ansible to reevaluate contents of an imported playbook as a task after main playbook that does the importing has started

Please consider the following scenario. An Ansible playbook, that uses a serries of ansible.builtin.import_playbook module calls, to chain other ansible playbooks together. We'll call this playbook main.yaml
...
- name: Import Ansible Playbook A
ansible.builtin.import_playbook: playbook_a.yaml
- name: Import Ansible Playbook B
ansible.builtin.import_playbook: playbook_b.yaml
...
My playbook_a.yaml calls a bash script, that eventually makes some changes in playbook_b.yaml using sed. In this current iteration, once main.yaml is started, my understanding is it reads the contents of all the files, including those imported via the ansible.builtin.import_playbook module, at the start of the playbook run, so any changes introduced into of those files after the start are not considered and are ignored. Essentially it plays them as they were, and not as they have become during the play.
My question is, is there a way for me to force main.yaml to reevaluate the conets of playbook_b.yaml, perhaps by adding another task between the import of playbook_a.yaml and playbook_b.yaml that would accomplish this?
Obviously I can run playbook_a.yaml seperatly, before starting main.yaml to avoid this issue, but my hope is to contain it all in one play.
TIA
It's not possible to reread imported playbooks. Instead, you can modify included tasks in the imported playbooks. For example, given the tree
shell:> tree .
.
├── ansible.cfg
├── hosts
├── pb_a.yml
├── pb_b.yml
├── pb.yml
├── tasks
│   ├── a_tasks.yml
│   └── b_tasks.yml
└── templates
├── tasks_1.yml.j2
└── tasks_2.yml.j2
Create the playbooks
shell> cat pb.yml
- hosts: localhost
vars:
playbook_updates:
- {dest: tasks/a_tasks.yml, template: tasks_1.yml.j2}
- {dest: tasks/b_tasks.yml, template: tasks_2.yml.j2}
tasks:
- name: Update playbooks
template:
src: "{{ item.template }}"
dest: "{{ playbook_dir }}/{{ item.dest }}"
loop: "{{ playbook_updates }}"
- import_playbook: pb_a.yml
- import_playbook: pb_b.yml
shell> cat pb_a.yml
- hosts: localhost
tasks:
- include_tasks: tasks/a_tasks.yml
shell> cat pb_b.yml
- hosts: localhost
tasks:
- include_tasks: tasks/b_tasks.yml
The first play updates the tasks in the imported playbooks according to the list playbook_updates. Create the templates, e.g.
shell> cat templates/tasks_1.yml.j2
- debug:
msg: tasks 1
shell> cat templates/tasks_2.yml.j2
- debug:
msg: tasks 2
Then, the playbook gives
shell> ansible-playbook pb.yml
PLAY [localhost] ****************************************************************************
TASK [Update playbooks] *********************************************************************
changed: [localhost] => (item={'dest': 'tasks/a_tasks.yml', 'template': 'tasks_1.yml.j2'})
changed: [localhost] => (item={'dest': 'tasks/b_tasks.yml', 'template': 'tasks_2.yml.j2'})
PLAY [localhost] ****************************************************************************
TASK [include_tasks] ************************************************************************
included: /export/scratch/tmp7/test-100/tasks/a_tasks.yml for localhost
TASK [debug] ********************************************************************************
ok: [localhost] =>
msg: tasks 1
PLAY [localhost] ****************************************************************************
TASK [include_tasks] ************************************************************************
included: /export/scratch/tmp7/test-100/tasks/b_tasks.yml for localhost
TASK [debug] ********************************************************************************
ok: [localhost] =>
msg: tasks 2
PLAY RECAP **********************************************************************************
localhost: ok=5 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
You can modify the included tasks by modifying the templates and variables.
After some more testing I have come up with two solutions that accomplish what I was hoping to accomplish. One is more of a a hacky solution and the other more of a proper Ansible solution.
Hacky Solution
Back in the main.yaml, instead of importing playbook_b.yaml directly, we import a new playbook, let's call it playbook_c.yaml,
...
- name: Import Ansible Playbook A
ansible.builtin.import_playbook: playbook_a.yaml
- name: Import Ansible Playbook C
ansible.builtin.import_playbook: playbook_c.yaml
...
which in turn only contains the following task:
...
tasks:
1. name: Call Playbook B
ansible.builtin.shell: ansible-playbook playbook_b.yaml
args:
executable: /bin/bash
register: call_playbook_b
changed_when: call_playbook_b != 0
Since ansible will not know any information regading playbook_b.yaml until playbook_c.yaml calls it using a bash command of ansible-playbook playbook_b.yaml, any changes introduced into playbook_b.yaml by playbook_a.yaml will be respected at the time the playbook is called. This feels cheeky but it works, with one major drawback, we don't get to see the output of playbook_b.yaml. We only see the output of playbook_a.yaml and playbook_c.yaml when we run main.yaml
Proper Solution
We leave our main.yaml as is
...
- name: Import Ansible Playbook A
ansible.builtin.import_playbook: playbook_a.yaml
- name: Import Ansible Playbook B
ansible.builtin.import_playbook: playbook_B.yaml
...
but modify the bash script called by playbook_a.yaml to make the sed changes not into playbook_b.yaml directly, but into the inventory file described in ansible.cfg, essentially adding a variable to the host(s) that playbook_b.yaml is playing against, like a dynamic inventory file. Then we add a pre_tasks to playbook_b.yaml before the tasks section, like so
...
pre_tasks:
- name: Refresh Inventory File
ansible.builtin.meta: refresh_inventory
tasks:
...
forcing ansible to reread the inventory file taking into the account the new values of the variables that were seded by the bash script called by playbook_a.yaml. Afterwards the variables can easily be referenced in playbook_b.yaml and they will take the latest value introduced after the start of main.yaml and playbook_a.yaml.

How can we execute only new task in Ansible playbook?

We have bit huge ansible tasks in main.yaml and we don't need to execute all tasks, only new task 'House Keep Task' is enough. So tried '--start-at-task' option as below, but Ansible can't find that task;
command :
ansible-playbook -u sysadmin -i ./inventories/dev/hosts --start-at-task='House Keep Task' --step batchservers.yml -K
message :
[ERROR]: No matching task "House Keep Task" found. Note: --start-at-task can only follow static includes.
batchserver.yaml
---
- hosts: batchservers
become: true
tasks:
- import_role:
name: batchservers
tags: [batch]
ansible/roles/batchservers/tasks/main.yaml
---
- name: Add SHARED_DATA_PATH env
lineinfile:
dest: ~/.bash_profile
line: export SHARED_DATA_PATH=/data
- name: Create /data if it does not exist
file:
path: /data
state: directory
mode: og+w
... other tasks include reboot task ...
- name: House Keep Task
cron:
name: "House Keep Task"
user: "{{ batch_user }}"
special_time: daily
job: "/usr/bin/find /var/log -name '*.log' -mtime +6 -type f -delete"
state: present
Is there any good way to execute particular task, House Keep Task?
Our ansible version is core 2.11.12.
Any advice would be highly apprciated.
Q: "Execute particular task House Keep Task."
A: The ansible-playbook option --start-at-task works as expected
--start-at-task 'START_AT_TASK'
start the playbook at the task matching this name
Given the tree
shell> tree .
.
├── ansible.cfg
├── hosts
├── pb.yml
└── roles
└── batchservers
└── tasks
└── main.yaml
3 directories, 4 files
The playbook
shell> cat pb.yml
- hosts: localhost
gather_facts: false
tasks:
- import_role:
name: batchservers
and the role
shell> cat roles/batchservers/tasks/main.yaml
- name: Add SHARED_DATA_PATH env
debug:
msg: Add SHARED_DATA_PATH env
- name: Create /data if it does not exist
debug:
msg: Create /data if it does not exist
- name: House Keep Task
debug:
msg: House Keep Task
give
shell> ansible-playbook pb.yml --start-at-task 'House Keep Task'
PLAY [localhost] *****************************************************************************
TASK [batchservers : House Keep Task] ********************************************************
ok: [localhost] =>
msg: House Keep Task
PLAY RECAP ***********************************************************************************
localhost: ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

How to parses multiple variables within ansible [duplicate]

I have an Ansible playbook like the one below, I want use nested variable like this:
msg={{{{Component}}.community_release_num}}, but when I run playbook:
ansible-playbook vartest.yml -e 'version=version_402', it not work
[es#vpn-server nested-var]$ tree
.
├── vars
│   ├── horizon.yml
│   └── version_402.yml
└── vartest.yml
1 directory, 3 files
[es#vpn-server nested-var]$ cat vartest.yml
---
- name: test
hosts: localhost
vars_files:
- vars/{{version}}.yml
tasks:
- debug: msg={{{{Component}}.community_release_num}}
- debug: msg={{{{Component}}.release_num}}
[es#vpn-server nested-var]$ cat vars/horizon.yml
Component: horizon
[es#vpn-server nested-var]$ cat vars/version_402.yml
- horizon:
community_release_num: '9.0.1'
release_num: '4.0.2'
[es#vpn-server nested-var]$
error messages
[es#vpn-server nested-var]$ ansible-playbook vartest.yml -e 'version=version_402'
/usr/lib64/python2.6/site-packages/cryptography/__init__.py:25: DeprecationWarning: Python 2.6 is no longer supported by the Python core team, please upgrade your Python.
DeprecationWarning
PLAY [test] *******************************************************************************************************
/usr/lib64/python2.6/site-packages/Crypto/Util/number.py:57: PowmInsecureWarning: Not using mpz_powm_sec. You should rebuild using libgmp >= 5 to avoid timing attack vulnerability.
_warn("Not using mpz_powm_sec. You should rebuild using libgmp >= 5 to avoid timing attack vulnerability.", PowmInsecureWarning)
TASK [debug] ******************************************************************************************************
fatal: [localhost]: FAILED! => {"failed": true, "msg": "template error while templating string: expected token 'colon', got '}'. String: {{{{Component}}.community_release_num}}"}
to retry, use: --limit #/data/wangqian/artemis-code-test/artemis/ansible/update/nested-var/vartest.retry
PLAY RECAP ********************************************************************************************************
localhost : ok=0 changed=0 unreachable=0 failed=1
Can Ansible use nested variable, if yes, how to use it?
Per Ansible FAQ:
Another rule is ‘moustaches don’t stack’. We often see this:
{{ somevar_{{other_var}} }}
The above DOES NOT WORK, if you need to use a dynamic variable use the
hostvars or vars dictionary as appropriate:
{{ hostvars[inventory_hostname]['somevar_' + other_var] }}
So in your case:
- debug: msg={{hostvars[inventory_hostname][Component].community_release_num}}
Or:
- debug: msg={{vars[Component].community_release_num}}
Or (since Ansible 2.5):
- debug: msg={{(lookup('vars', Component)).community_release_num}}
Run this command:
ansible-playbook 1_lambda-facts.yml -e "func_name=mylambdaFunctionName"
- name: get lamda Info
hosts: localhost
connection: local
become: yes
gather_facts: true
tasks:
- name: List all for a specific function
lambda_facts:
query: all
function_name: "{{func_name}}"
- name: debug info
debug:
msg: "{{lambda_facts}}"
msg: "variable name is: {{vars['func_name']}}"
msg: "{{lambda_facts['function'][vars['func_name']]['function_arn']}}"

How can I use Ansible nested variable?

I have an Ansible playbook like the one below, I want use nested variable like this:
msg={{{{Component}}.community_release_num}}, but when I run playbook:
ansible-playbook vartest.yml -e 'version=version_402', it not work
[es#vpn-server nested-var]$ tree
.
├── vars
│   ├── horizon.yml
│   └── version_402.yml
└── vartest.yml
1 directory, 3 files
[es#vpn-server nested-var]$ cat vartest.yml
---
- name: test
hosts: localhost
vars_files:
- vars/{{version}}.yml
tasks:
- debug: msg={{{{Component}}.community_release_num}}
- debug: msg={{{{Component}}.release_num}}
[es#vpn-server nested-var]$ cat vars/horizon.yml
Component: horizon
[es#vpn-server nested-var]$ cat vars/version_402.yml
- horizon:
community_release_num: '9.0.1'
release_num: '4.0.2'
[es#vpn-server nested-var]$
error messages
[es#vpn-server nested-var]$ ansible-playbook vartest.yml -e 'version=version_402'
/usr/lib64/python2.6/site-packages/cryptography/__init__.py:25: DeprecationWarning: Python 2.6 is no longer supported by the Python core team, please upgrade your Python.
DeprecationWarning
PLAY [test] *******************************************************************************************************
/usr/lib64/python2.6/site-packages/Crypto/Util/number.py:57: PowmInsecureWarning: Not using mpz_powm_sec. You should rebuild using libgmp >= 5 to avoid timing attack vulnerability.
_warn("Not using mpz_powm_sec. You should rebuild using libgmp >= 5 to avoid timing attack vulnerability.", PowmInsecureWarning)
TASK [debug] ******************************************************************************************************
fatal: [localhost]: FAILED! => {"failed": true, "msg": "template error while templating string: expected token 'colon', got '}'. String: {{{{Component}}.community_release_num}}"}
to retry, use: --limit #/data/wangqian/artemis-code-test/artemis/ansible/update/nested-var/vartest.retry
PLAY RECAP ********************************************************************************************************
localhost : ok=0 changed=0 unreachable=0 failed=1
Can Ansible use nested variable, if yes, how to use it?
Per Ansible FAQ:
Another rule is ‘moustaches don’t stack’. We often see this:
{{ somevar_{{other_var}} }}
The above DOES NOT WORK, if you need to use a dynamic variable use the
hostvars or vars dictionary as appropriate:
{{ hostvars[inventory_hostname]['somevar_' + other_var] }}
So in your case:
- debug: msg={{hostvars[inventory_hostname][Component].community_release_num}}
Or:
- debug: msg={{vars[Component].community_release_num}}
Or (since Ansible 2.5):
- debug: msg={{(lookup('vars', Component)).community_release_num}}
Run this command:
ansible-playbook 1_lambda-facts.yml -e "func_name=mylambdaFunctionName"
- name: get lamda Info
hosts: localhost
connection: local
become: yes
gather_facts: true
tasks:
- name: List all for a specific function
lambda_facts:
query: all
function_name: "{{func_name}}"
- name: debug info
debug:
msg: "{{lambda_facts}}"
msg: "variable name is: {{vars['func_name']}}"
msg: "{{lambda_facts['function'][vars['func_name']]['function_arn']}}"

Resources