Ansible nested braces or nested quotes - ansible

Using ansible 2.9 I set a variable like this to store part of a group name
- name: set group
set_fact:
ansible_group: aaaa
I then want to use this variable in the following with_items clause:
- name: get
uri:
url: "http://{{ item }}:5324/kjhfg"
with_items: "{{ groups['thisgroup_{{ ansible_group }}'] }}"
However, using nested curly braces gives me the following error:
FAILED! => {"msg": "'dict object' has no attribute 'thisgroup_{{ ansible_group }}'"}
I also tried the following syntax variations
with_items: "{{ groups['thisgroup_ansible_group'] }}"
with_items: "groups['thisgroup_{{ ansible_group }}']"
with_items: "{{ groups['thisgroup_hostvars['localhost']['ansible_group']'] }}"
with_items: "{{ groups['thisgroup_hostvars[''localhost''][''ansible_group'']'] }}"
with_items: "{{ groups['thisgroup_hostvars[`localhost`][`ansible_group`]'] }}"
and probably one hundred other variations which all of them produced various errors
Can someone help me figure out the right syntax?

Simply concatenate the name of the group, e.g. given the inventory
shell> cat hosts
[thisgroup_aaaa]
host1
host2
host3
the playbook
- hosts: all
gather_facts: false
vars:
ansible_group: aaaa
_group: "{{ groups['thisgroup_' ~ ansible_group] }}"
tasks:
- debug:
var: item
loop: "{{ _group }}"
run_once: true
gives
item: host1
item: host2
item: host3

Related

get ansible groups by looping with_items

I'm trying to get the inventory host group names by below process which is not working
- debug:
msg: "{{ groups['{{ item }}'] }}"
with_items: "{{ vm.stdout_lines }}"
this is what actually I'm trying to do
I will get a list of servers by a shell script
- name: Getting the servers list
shell: |
sh getServers.sh
register: vm
Then adding them into inventory by add_host
- name: Creating Logical host_group for each server
add_host:
name: "{{ item }}"
groups: ["{{item }}"]
with_items: "{{ vm.stdout_lines }}"
register: inv
Here I'm trying to get the only groups I've added in above step instead of all groups
- debug:
msg: "{{ groups['{{ item }}'] }}"
with_items: "{{ vm.stdout_lines }}"
Error is
{"msg": "The task includes an option with an undefined variable. The error was: 'dict object' has no attribute '{{ item }}'
Appreciate help on this!
Fix the syntax
msg: "{{ groups[item] }}"

ansible assign shell results specific hosts in hostvars using with_items

Basically i want modify an existing hostvars.
I have a dynamically generated array of hosts named "flash_hosts"
flash_hosts = ['host1', 'host2']
and a shell script which looks up a value for each host.
In following non-working code i try to assign each host the specific result of the script
- name: Assign values to to host var
shell: "get-assigned-value.sh {{ item }}"
register: "{{ hostvars[item].mac=stdout }}"
with_items: "{{ flash_hosts }}"
How can i make this work in in ansible? Basically i understand that register will not allow me to assign the value to hostvars directly, but how can this be solved then, as i need to iterate over the hosts?
Use set_fact to debug your register results on host wise..
- name: Assign values to to host var
shell: "/path/to/get-assigned-value.sh {{ item }}"
register: fileout
with_items:
- host1
- host2
- set_fact:
firstHost: "{{ fileout.results[0] }}"
secondHost: "{{ fileout.results[1] }}"
- debug:
var: firstHost.stdout
- debug:
var: secondHost.stdout
in the above example, firstHost is whole result of shell script running on first host, and firstHost.stdout gives the output of corresponding host shell script result.
It's possible set_facts and delegate_to the flash_hosts with delegate_facts.
But, to create a variable in hostvars the host must be declared in the inventory(static or dynamic). It's not necessary the host is accessible. For example
$ cat hosts
host1
host2
The play below
- hosts: localhost
vars:
flash_hosts: ['host1', 'host2']
tasks:
- name: Assign values to to host var
command: "{{ playbook_dir }}/get-assigned-value.sh {{ item }}"
register: result
loop: "{{ flash_hosts }}"
- set_fact:
mac: "{{ item.stdout }}"
loop: "{{ result.results }}"
delegate_to: "{{ item.item }}"
delegate_facts: true
- debug:
msg: "{{ hostvars[item].mac }}"
loop: "{{ flash_hosts }}"
gives
ok: [localhost] => (item=host1) =>
msg: mac address of host1
ok: [localhost] => (item=host2) =>
msg: mac address of host2
with the script
$ cat get-assigned-value.sh
#!/bin/sh
case $1 in
host1)
printf "mac address of host1"
;;
host2)
printf "mac address of host2"
;;
*)
printf "unknown host"
exit 1
;;
esac
exit 0

Issue passing variable to include_task file having add_host in Ansible

I am dealing with nested loops inorder to build dynamic host using add_host.
Outer Loop: with_items: "{{ command_result.stdout_lines }}" // gets me the list of users
Inner Loop: with_items: "{{ dest_ip.split(',') }}" // gets me the list of IP addresses seperated by comma (,)
The below playbook works fine.
- name: Add hosts
include_tasks: "{{ playbook_dir }}/gethosts.yml"
vars:
dest_ip: "{{ item.split('\t')[0] }}"
file_dets: "{{ item.split('\t')[1] }}"
USER_pass: "anystring"
# USER_pass: "{% if item.split('\t')[3] == 'FrontEnd' %}user1{% elif item.split('\t')[3] == 'BackEnd' %}user2{% else %}{% endif %}"
ansible_host: localhost
install_dir_pass: "anystring"
# install_dir_pass: "{{ item.split('\t')[2] }}"
with_items: "{{ command_result.stdout_lines }}"
Below is my include_task gethost.yml file:
---
- name: Generate JSON data
add_host:
name: "{{ item }}"
groups: dest_nodes
ansible_user: "{{ USER_pass }}"
install_dir: "{{ install_dir_pass }}"
with_items: "{{ dest_ip.split(',') }}"
I get the below error if I uncomment either USER_pass or install_dir_pass and comment the existing value:
TASK [Generate JSON data]
*********************************************************************************************************************************** task path: /app/deployment/gethosts.yml:2 [WARNING]: The loop
variable 'item' is already in use. You should set the loop_var value
in the loop_control option for the task to something else to avoid
variable collisions and unexpected behavior.
fatal: [localhost]: FAILED! => {
"msg": "The task includes an option with an undefined variable. The error was: list object has no element 2\n\nThe error appears to be
in '/app/deployment/gethosts.yml': line 2, column 4, but may\nbe
elsewhere in the file depending on the exact syntax problem.\n\nThe
offending line appears to be:\n\n---\n - name: Generate JSON data\n
^ here\n" }
NO MORE HOSTS LEFT
PLAY RECAP
************************************************************************************************************************************************** localhost : ok=12 changed=1 unreachable=0
failed=1 skipped=0 rescued=0 ignored=0
Requesting a solution to this issue and an explanation of a few questions I have.
The dest_ip is read and works fine with .split(,) method inside of include_task get_hosts.yml file when other variables like install_dir_pass dont seem to work.
When USER_pass and install_dir_pass are given simple string "AnyString" it works and is read fine inside of get_hosts.yml where as if they are assigned values using item.split('\t')[] the playbook errors as above.
I have already tested using debug that all the entries in command_result are good and the values should be populated correctly as below.
- debug:
msg: "{{ item.split('\t')[0] }}"
# msg: "{{ item.split('\t')[1] }}"
#msg: "{{ item.split('\t')[2] }}"
# msg: "{{ item.split('\t')[3] }}"
with_items: "{{ command_result.stdout_lines }}"
As this is very well explained in your error message:
There is a name collision between the item of each nested loops.
You have to set the loop_var name in the loop_control option to resolve the conflict in either of the loops.
I suggest you do this on the include_task so that it solves the problem for any other loop inside your included file. As I have no idea what your command_result.stdout_lines contains exactly, I used in the below example the var name my_result that you should change with something fitting your situation. Note that this var will be available directly in the included file if necessary.
- name: Add hosts
include_tasks: "{{ playbook_dir }}/gethosts.yml"
vars:
dest_ip: "{{ my_result.split('\t')[0] }}"
file_dets: "{{ my_result.split('\t')[1] }}"
USER_pass: "{% if my_result.split('\t')[3] == 'FrontEnd' %}user1{% elif my_result.split('\t')[3] == 'BackEnd' %}user2{% else %}{% endif %}"
ansible_host: localhost
install_dir_pass: "{{ my_result.split('\t')[2] }}"
with_items: "{{ command_result.stdout_lines }}"
loop_control:
loop_var: my_result

Variable with_items and hostvars

I need to pass a dynamic group name info with_items so that i can access a specific fact that is ran from another host. I cannot hard code the group name
I tried to set a generic variable that is passed as 'GroupName' a few different ways. Including
with_items: "{{ groups['{{GROUPNAME}}'] }}"
- name: Name of task
debug:
msg: "{{ hostvars[item]['ansible_check_mode'] }}"
with_items: "{{ groups['GROUPNAME'] }}"
fatal: [localhost]: FAILED! => {"msg": "'dict object' has no attribute '{{ GROUPNAME }}'"}
Get the list of the hosts in the group and loop them
vars:
my_group: GROUPNAME
tasks:
- set_fact:
my_hosts: "{{ groups|
dict2items|
selectattr('key', 'match', my_group)|
map(attribute='value')|
list|
flatten }}"
- debug:
msg: "{{ hostvars[item]['ansible_check_mode'] }}"
loop: "{{ my_hosts }}"
(not tested)

How to create a 'null' default in Ansible

I want 'lucy' to follow the user module creators' default behaviour which is to create and use a group matching the user name 'lucy'. However for 'frank' I want the primary group to be an existing one; gid 1003. So my hash looks like this:
lucy:
comment: dog
frank:
comment: cat
group: 1003
And my task looks like this:
- name: Set up local unix user accounts
user:
name: "{{ item.key }}"
comment: "{{ item.value.comment }}"
group: "{{ item.value.group | default(undef) }}"
loop: "{{ users|dict2items }}"
This doesn't work, as undef is not recognised. Nor is anything else I can think of. 'null', 'None' etc. all fail. '' creates an empty string which is not right either. I can't find out how to do it.
Any ideas?
default(omit) is what you are looking for. For example,
- name: Set up local Unix user accounts
user:
name: "{{ item.key }}"
comment: "{{ item.value.comment }}"
group: "{{ item.value.group | default(omit) }}"
loop: "{{ users|dict2items }}"
Comments
Comment by Lucas Basquerotto: "... omit only works correctly when used directly in a module, it won't work in a set_fact ..."
A: You're wrong. For example, default(omit) works both in set_fact and in the module. The first item in the list defaults to false with the result "VARIABLE IS NOT DEFINED!". The second item defaults to omit. Omitted parameter get_checksum defaults to true with the checksum in the results
shell> cat pb.yml
- hosts: localhost
tasks:
- set_fact:
test:
- "{{ gchk|default(false) }}"
- "{{ gchk|default(omit) }}"
- stat:
path: /etc/passwd
get_checksum: "{{ item }}"
loop: "{{ test }}"
register: result
- debug:
var: item.stat.checksum
loop: "{{ result.results }}"
gives
shell> ansible-playbook pb.yml | grep item.stat.checksum
item.stat.checksum: VARIABLE IS NOT DEFINED!
item.stat.checksum: 7c73e9f589ca1f0a1372aa4cd6944feec459c4a8
In addition to this, default(omit) works as expected also in some expressions. For example
- debug:
msg: "{{ {'a': item}|combine({'b': true}) }}"
loop: "{{ test }}"
gives
msg:
a: false
b: true
msg:
b: true
See the results without default values
shell> ansible-playbook pb.yml -e "gchk={{ true|bool }}"

Resources