Is it possible to set variable in variable with ansible? - ansible

For example, set a username for each different service as
- name: Add the user 'user_xxx' with password
ansible.builtin.user:
name: "{{ user_name_{{svc_name}} }}"
password: "{{ user_password_{{svc_name}} | password_hash('sha512') }}"
In order to set dynamic values to name and password, use thess variables in group_vars:
user_name_a
user_name_b
user_name_c
user_password_a
user_password_b
user_password_c
But want to read a, b, c from an environment variable as:
export SERVICE_NAME=a
playbook:
vars:
svc_name: "{{ lookup('env', 'SERVICE_NAME') }}"
Then compile 2 kinds of variables together to make a final value.
When I tried to do it, I got
fatal: [server]: FAILED! => {"msg": "template error while templating string: expected token 'end of print statement', got '{'. String: {{ user_name_{{svc_name}} }}"}

For example, given the group_vars
shell> cat group_vars/all.yml
user_name_a: userA
user_name_b: userB
user_name_c: userC
user_password_a: psswdA
user_password_b: psswdB
user_password_c: psswdC
the playbook
shell> cat playbook.yml
- hosts: localhost
vars:
svc_name: "{{ lookup('env', 'SERVICE_NAME') }}"
tasks:
- debug:
msg: "name: {{ lookup('vars', 'user_name_' ~ svc_name) }},
pswd: {{ lookup('vars', 'user_password_' ~ svc_name) }}"
gives
shell> SERVICE_NAME=b ansible-playbook playbook.yml
PLAY [localhost] ****************************************************************
TASK [debug] ********************************************************************
ok: [localhost] =>
msg: 'name: userB, pswd: psswdB'
...

Related

Variables not expended inside ansible.builtin.include_vars

I have this following file var/Hendrix.yml
last: Hendrix
fullname: "Jimi {{ last }}"
And the following playbook
- name:
hosts: localhost
tasks:
- name: include
include_vars: "{{ choice }}.yml"
- name: debug
debug:
msg: "include_vars {{ choice }}, {{ fullname }}"
tags: simpleinc
- name:
hosts: localhost
tags: builtininc
tasks:
- name: include
ansible.builtin.include_vars:
file: "{{ choice }}.yml"
- name: debug
debug:
msg: "ansible.builtin.include_vars {{ choice }}, {{ fullname }}"
running with tags simpleinc or builtininc does not show the same result
ansible-playbook test.yml --extra-vars "choice=Hendrix" --tags simpleinc
TASK [debug] *******************************************************************
ok: [localhost] => {
"msg": "include_vars Hendrix, Jimi Hendrix"
}
ansible-playbook test.yml --extra-vars "choice=Hendrix" --tags builtininc
TASK [debug] *******************************************************************
ok: [localhost] => {
"msg": "ansible.builtin.include_vars Hendrix, Jimi {{ last }}"
}
I read that using full name of task is a good practice, but here it puzzeld me a bit, maybe this is due to file parameter, but I don't see any template

Ansible lint : Found a bare variable

This is my ansible script.
- name: Validate that blacklisted URLs are unreachable
environment:
SSL_CERT_FILE: "{{ ssl_cert_path }}"
ansible.builtin.uri:
url: "{{ item }}"
timeout: 10
register: blacklisted_http_responses
with_lines: cat {{ role_path }}/files/blacklisted_urls.txt
And i am getting this lint error for the sbove code
Found a bare variable 'cat {{ role_path }}/files/blacklisted_urls.txt' used in a 'with_lines' loop.
any idea how to resolve this ? I tried Putting the variable name in double quotes.
What you see is very probably an ansible-lint issue. You should use loop instead of with_lines. There are no complaints by ansible-lint about the code below
loop: "{{ lookup('file',
role_path ~ '/files/blacklisted_urls.txt').splitlines() }}"
You can also use the pipe lookup plugin instead of the file if you want to. The loop below gives the same result
loop: "{{ lookup('pipe',
'cat ' ~ role_path ~ '/files/blacklisted_urls.txt').splitlines() }}"
For example, the playbook
shell> cat pb.yml
---
- hosts: localhost
roles:
- role_a
the role
shell> cat roles/role_a/tasks/main.yml
---
- name: Debug
debug:
var: item
loop: "{{ lookup('file',
role_path ~ '/files/blacklisted_urls.txt').splitlines() }}"
and the file
shell> cat roles/role_a/files/blacklisted_urls.txt
www1.example.com
www2.example.com
give (abridged)
TASK [role_a : Debug] ****************************************************
ok: [localhost] => (item=www1.example.com) =>
ansible_loop_var: item
item: www1.example.com
ok: [localhost] => (item=www2.example.com) =>
ansible_loop_var: item
item: www2.example.com

Issue with using Omit option

I am trying configure a attribute only if my_int_http is defined else I dont want it. So I coded it like below:
profiles:
- "{{ my_int_L4 }}"
- "{{ my_int_http | default(omit) }}"
However my execution fail and when check the arguments passed by the code in actual configuration it shows like below:
"profiles": [
"my_example.internal_tcp",
"__omit_place_holder__ef8c5b99e9707c044ac07fda72fa950565f248a4"
So how to pass absolutely no value where it is passing __omit_place_holder_****?
Q: "How to pass absolutely no value where it is passing omit_place_holder ?"
A1: Some filters also work with omit as expected. For example, the play
- hosts: localhost
vars:
test:
- "{{ var1|default(false) }}"
- "{{ var1|default(omit) }}"
tasks:
- debug:
msg: "{{ {'a': item}|combine({'b': true}) }}"
loop: "{{ test }}"
gives
msg:
a: false
b: true
msg:
b: true
As a sidenote, default(omit) is defined type string
- debug:
msg: "{{ item is defined }}"
loop: "{{ test }}"
- debug:
msg: "{{ item|type_debug }}"
loop: "{{ test }}"
give
TASK [debug] *************************************************************
ok: [localhost] => (item=False) =>
msg: true
ok: [localhost] => (item=__omit_place_holder__6e56f2f992faa6e262507cb77410946ea57dc7ef) =>
msg: true
TASK [debug] *************************************************************
ok: [localhost] => (item=False) =>
msg: bool
ok: [localhost] => (item=__omit_place_holder__6e56f2f992faa6e262507cb77410946ea57dc7ef) =>
msg: str
A2: No value in Ansible is YAML null. Quoting:
This is typically converted into any native null-like value (e.g., undef in Perl, None in Python).
(Given my_int_L4=bob). If the variable my_int_http defaults to null instead of omit
profiles:
- "{{ my_int_L4 }}"
- "{{ my_int_http | default(null) }}"
the list profiles will be undefined
profiles: VARIABLE IS NOT DEFINED!
Use None instead
profiles:
- "{{ my_int_L4 }}"
- "{{ my_int_http | default(None) }}"
The variable my_int_http will default to an empty string
profiles:
- bob
- ''
See also section "YAML tags and Python types" in PyYAML Documentation.
You can try something like this,
profiles:
- "{{ my_int_L4 }}"
- "{{ my_int_http | default(None) }}"
This will give you an empty string. And you can add a check while iterating over the profiles.
Please have a look at this GitHub Issue to get more understanding.

Ansible escape string \

Trying to add \ before . on a list of IPs in Ansible.
Example:
"msg": "192.168.5.0"
Expected output:
"msg": "192\.168\.5\.0"
I tried this with no luck.
---
- hosts: localhost
vars:
ip: "{{ ansible_default_ipv4.network }}"
tasks:
- debug: msg="{{ ip | regex_replace('\.', '\\.')}}"
Output:
ok: [localhost] => {
"msg": "192\\.168\\.5\\.0"
}
---
- hosts: localhost
vars:
ip: "{{ ansible_default_ipv4.network }}"
tasks:
- debug: msg="{{ ip | regex_replace('.', '\.')}}"
Output:
ok: [localhost] => {
"msg": "192\\.168\\.5\\.0"
}
The function regex_replace works as you expect. The double backslashes you see are the evaluation of the string by debug. Display, for example, the length of the string
- set_fact:
ip2: "{{ ip | regex_replace(myregex, myreplace) }}"
vars:
myregex: '\.'
myreplace: '\.'
- debug:
msg: "length of {{ ip2 }} is {{ ip2|length }}"
give
"msg": "length of 192\\.168\\.5\\.0 is 14"
It's also possible to write the string to a file. For example the template
shell> cat test.txt.j2
{{ ip2 }}
and the task
- template:
src: test.txt.j2
dest: test.txt
give
shell> cat test.txt
192\.168\.5\.0

Ansible - Read file and register variable

I have a file with the following content:
AWS_ACCESS_KEY_ID=xxxxxxx
AWS_SECRET_ACCESS_KEY=yyyyyy
AWS_SESSION_TOKEN=zzzzzzzz
How do I read this file, split the line based on "=" and set the values of the variables (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN) so that I can use these variables in the script for later use?
Q: How do I read this file, split the line based on "=" and set the values of the variables?
A: Use ini lookup plugin. For example the tasks below
- set_fact:
AWS_ACCESS_KEY_ID: "{{ lookup('ini', 'AWS_ACCESS_KEY_ID type=properties file=conf.ini') }}"
- debug:
var: AWS_ACCESS_KEY_ID
give
"AWS_ACCESS_KEY_ID": "xxxxxxx"
It is possible to use a list of variables. For example the play below
- hosts: localhost
vars:
my_vars_keys: [AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN]
tasks:
- set_fact:
my_vars: "{{ my_vars|default({})|
combine({item:
lookup('ini',
item ~ ' type=properties file=conf.ini')})
}}"
loop: "{{ my_vars_keys }}"
- debug:
msg: "{{ my_vars[item] }}"
loop: "{{ my_vars_keys }}"
gives
ok: [localhost] => (item=AWS_ACCESS_KEY_ID) => {
"msg": "xxxxxxx"
}
ok: [localhost] => (item=AWS_SECRET_ACCESS_KEY) => {
"msg": "yyyyyy"
}
ok: [localhost] => (item=AWS_SESSION_TOKEN) => {
"msg": "zzzzzzzz"
}
Q: How do I make sure that above set_fact runs on the hosts and not on the ansible tower?
A: The set_fact uses Lookup Plugins. Quoting
Like all templating, these plugins are evaluated on the Ansible control machine, not on the target/remote.

Resources