ansible 1.8 does not resolve item.key in set_facts - ansible

So i have the below section in my playbook:
- name: Loop hash
set_fact:
"{{ item.key }}":
instance_id: "{{ item.value.ansible_ec2_instance_id }}"
instance_az: "{{ item.value.ansible_ec2_placement_availability_zone }}"
with_dict: "{{ hostvars }}"
when: (item.value.ansible_ec2_instance_id is defined) and
(item.value.ansible_ec2_placement_availability_zone is defined)
when I check the output it is not resolving item.key
'item.key': {'instance_id': u'i-abc12345678', 'instance_az': u'ap-southeast-2b'}
any ideas on whats going on?
edit:
error for suggested solution:
ERROR: Syntax Error while loading YAML script, /apps/co-playbooks/common/tasks/elb_check_instances.yml
Note: The error may actually appear before this position: line 40, column 18
{ "{{ item.key }}":
instance_id: "{{ item.value.ansible_ec2_instance_id }}"
^
We could be wrong, but this one looks like it might be an issue with
missing quotes. Always quote template expression brackets when they
start a value. For instance:
tried different syntax:
- name: Loop hash
set_fact:
{ "{{ item.key }}":
{ instance_id: "{{ item.value.ansible_ec2_instance_id }}",
instance_az: "{{ item.value.ansible_ec2_placement_availability_zone }}" }}
with_dict: "{{ hostvars }}"
when: (item.value.ansible_ec2_instance_id is defined) and
(item.value.ansible_ec2_placement_availability_zone is defined)
still does not resolve item.key
'{# item.key #}': {'instance_id': u'i-abc1234567', 'instance_az': u'ap-southeast-2b'}

Add one pair of { } outside the set_fact task.
- name: Loop hash
set_fact:
{ "{{ item.key }}":
instance_id: "{{ item.value.ansible_ec2_instance_id }}"
instance_az: "{{ item.value.ansible_ec2_placement_availability_zone }}" }
with_dict: "{{ hostvars }}"
when: (item.value.ansible_ec2_instance_id is defined) and
(item.value.ansible_ec2_placement_availability_zone is defined)

Related

Ansible "item is undefined" when using {{ item }} in "vars" section

I have some codes like below, I want to iterate each host in groups['A'] and groups['B'] to create the group.
- name: Create a group
group:
name: "test_group"
state: "present"
delegate_to: "{{ item }}"
vars:
ansible_ssh_user: "{{ lookup('env', 'USER') }}#user#{{ item }}"
with_items:
- "{{ groups['A'] }}"
- "{{ groups['B'] }}"
Because I want to modify the ansible ssh connection user to connect to the "delegate_to" host, I override the ansible_ssh_user in this task, but it won't work and give me the error message like
FAILED! => {"msg": "'item' is undefined"}
But if I comment out the lines of
vars:
ansible_ssh_user: "{{ lookup('env', 'USER') }}#user#{{ item }}"
It gives no errors.
try changing the var to:
ansible_ssh_user: "{{ lookup('env', 'USER') }}#user#{{ ansible_hostname }}
this works fine for me:
- name: 'install public key on every server'
authorized_key:
user: '{{ myuser}}'
key: "{{ myuser.ssh_public_key }}"
delegate_to: '{{ item }}'
with_items:
- '{{ groups["A"] }}'
- '{{ groups["B"] }}'
- '{{ groups["C"] }}'
maybe you could try
but before I delegated a set_fact and recovered it locally...
Finally, I got a workaround, it involves all groups I need to use in my playbook.
And use "when" condition like
delegate_to: "{{ item }}"
with_items:
- "{{ groups['A'] }}"
- "{{ groups['B'] }}"
when: "inventory_hostname == item"
to let the task only run on the hosts mentioned in the "with_items" section.
It's not a very cool workaround but works for me...
Thank you for looking at this problem!

Ansible - skip undefined variable in dict

I`m using ipa_user module to setup users. There is variable passsword which force new password.
For some users (when var is not in dict) I would like to skip it in iteration, but it always fail.
This is snippet from my playbook. Ansible version is 2.7
task:
- name: adding ipa users
ipa_user:
name: "{{ item.value.login }}"
state: "{{ item.value.state }}"
givenname: "{{ item.value.givenname }}"
sn: "{{ item.value.surname }}"
mail: "{{ item.value.mail }}"
telephonenumber: "{{ item.value.telephonenumber }}"
title: "{{ item.value.title }}"
password: "{{ item.value.password }}" <<- to be skipped if not found
ipa_host: ipa.gdi.telekom.de
ipa_user: admin
ipa_pass: "{{ ipa_pass }}"
with_dict: "{{ipausers}}"
when: item.key in ipausers.keys()
register: output_ipa_users
Log:
fatal: [localhost]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'dict object' has no attribute 'password'\n\nThe error appears to have been in '/builds/gitlab/infra/user-management/roles/free-ipa/tasks/main.yml': line 13, column 3, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n- name: adding ipa users\n ^ here\n"}
Note: I tried it with:
with_dict: "{{ipausers|default({})}}"
ignore_errors: yes
without success
Not sure if it'll be much help to you now but for others than stumble on this post, I ended up with something like below for a similar problem. I'm using Ansible 2.7.8.
- name: Creating user accounts...
user:
name: "{{ item.name }}"
state: "{{ item.state }}"
comment: "{{ item.comment | default(omit) }}"
group: "{{ item.groups is defined | ternary((item.groups|default([]))[0], omit) }}"
groups: "{{ item.groups | default(omit) }}"
password: "{{ item.password_hash | default(omit) }}"
uid: "{{ item.uid | default(omit) }}"
with_items: "{{ managed_users }}"
The solution is
group: "{{ item.groups is defined | ternary((item.groups|default([]))[0], omit) }}"
If groups isn't in item then Ansible will omit the group part of this tasks but jinja2 will evaluate item.groups[0] anyway. So to allow for this we have to use item.groups|default([]) so jinja2 uses an empty list when groups isn't defined instead of throwing a 'dict object' has no attribute error. The omit part is similar to the default(omit) filter where Ansible simply omits the option from the task.
Lubo's problem is a little simpler so using just default(omit) filter should work. That said as password is required so the entire task should be skipped with a conditional.
- name: adding ipa users
ipa_user:
name: "{{ item.value.login }}"
state: "{{ item.value.state }}"
givenname: "{{ item.value.givenname }}"
sn: "{{ item.value.surname }}"
mail: "{{ item.value.mail }}"
telephonenumber: "{{ item.value.telephonenumber }}"
title: "{{ item.value.title }}"
password: "{{ item.value.password | default(omit) }}" #<-- would be omitted
ipa_host: ipa.gdi.telekom.de
ipa_user: admin
ipa_pass: "{{ ipa_pass }}"
with_dict: "{{ipausers}}"
when: item.key in ipausers.keys() and item.key.password is defined #<-- second check for when password is not defined.
register: output_ipa_users
If you want to completely skip the ipa_user module execution when password is not defined, check for its presence in your when clause:
when: item.value.password | default('') | length > 0
If you want to execute the ipa_user module without specifying a password for user if it does not exists, use the omit placeholder in your module params:
password: "{{ item.value.password | default(omit) }}"
Note: your current when clause can be removed. It will always return true as you are looping over a dict and later checking if the current key in the loop is part of that dict.
There is a special omit variable to omit module parameters.
password: "{{ item.value.password|default(omit) }}"
To make a playbook or a role reusable it is a good idea to declare all parameters of a module in the task and default(omit) parameters that are not required.

Loop through variables in task that has another list embedded in the variables

I am looking to loop through a list of variables. I have it looping through the of variables using with_items, however the catch is there is a list within that variables list that needs to have a different subset / number of variables that i need to iterate through as well.
I have tried different filters to include with_nested, with_subelements, and with_items. I know that they are moving towards loops as the primary driver moving forward so any solution ideally would leverage the ansible path moving forward. I am looking at having an "inner" loop or an external task that will iterate through the vlans_list and input that data as its to that point.
group Variables
vnic_templates:
- name: vNIC-A
fabric: A
mac_pool: testmac1
mtu: 1500
org_dn: org-root
redundancy_type: none
state: present
template_type: initial-template
vlans_list: ### THE PROBLEM CHILD
- name: vlan2
native: 'no'
state: present
- name: vlan3
native: 'no'
state: present
The actual task - i have issues when i have to input multiple vlans. The vnic template will have a 1 to one relationship however the vlans_list could be 1 vnic_template to many vlans.
ucs_vnic_template:
hostname: "{{ ucs_manager_hostname }}"
username: "{{ ucs_manager_username }}"
password: "{{ ucs_manager_password }}"
name: "{{ item.name }}"
fabric: "{{ item.fabric }}"
mac_pool: "{{ item.mac_pool }}"
mtu: "{{ item.mtu }}"
org_dn: "{{ item.org_dn }}"
redundancy_type: "{{ item.redundancy_type }}"
state: "{{ item.state }}"
template_type: "{{ item.template_type }}"
vlans_list:
- name: "{{ item.1.name }}"
native: "{{ item.1.native }}"
state: "{{ item.1.present }}"
# loop: "{{ vnic_templates | subelements('vlans_list') }}"
with_items:
- "{{ vnic_templates }}"
I am starting down the road of adding an include vlan_list.yml outside of this task but no familiar with out to do that.
Actual results are
The task includes an option with an undefined variable. The error was: 'item' is undefined\n\n
I need the create a single vnic template with multiple vlans defined in that list.
Another engineer i work with was able to solve the question. By the way the variables are laid out we were able to easily just change the code
Change this:
vlans_list:
- name: "{{ item.1.name }}"
native: "{{ item.1.native }}"
state: "{{ item.1.present }}"
To this:
vlans_list: "{{ item.vlans_list }}"
Full Code listed below.
- name: Add vNIC Templates
ucs_vnic_template:
hostname: "{{ ucs_manager_hostname }}"
username: "{{ ucs_manager_username }}"
password: "{{ ucs_manager_password }}"
name: "{{ item.name }}"
fabric: "{{ item.fabric }}"
mac_pool: "{{ item.mac_pool }}"
mtu: "{{ item.mtu }}"
org_dn: "{{ item.org_dn }}"
redundancy_type: "{{ item.redundancy_type }}"
state: "{{ item.state }}"
template_type: "{{ item.template_type }}"
vlans_list: "{{ item.vlans_list }}"
with_items:
- "{{ vnic_templates }}"

Ansible condition on dictionary item

I have one dictionary list defined:
members_list:
any:
name: any
username: any
anyone:
name:
username: anyone
In my Ansible playbook, I am running multiple docker containers:
docker_container:
name: "{{ item.key }}"
when: "{{ item.key }} != any"
with_dict: "{{ members_list }}"
But I am getting following error:
The conditional check '{{ item.key }} != any' failed. The
error was: error while evaluating conditional ({{ item.key
}} != any): 'any' is undefined
When I run the playbook without when conditional operator it works fine.
Thanks in advance.
You need to quote the word any, because otherwise it is interpreted as a variable reference. Also, you don't need Jinja templating markers in a when expression (the expression is already evaluated as a jinja expression):
- docker_container:
name: "{{ item.key }}"
when: "item.key != 'any'"
with_dict: "{{ members_list }}"
I find nested quotes like this can be confusing, and often opt for one
of YAMLs alternative quoting mechanisms, e.g.:
- docker_container:
name: "{{ item.key }}"
when: >-
item.key != 'any'
with_dict: "{{ members_list }}"
This is identical to the previous version; I just find it easier to
read and understand.

Setting up correctly gateway_ip in Ansible function

I am using the following function to deploy an Openstack subnet using Ansible and variable file:
- name: Create the subnets
os_subnet:
cloud: "{{ item.cloud }}"
state: present
validate_certs: no
gateway_ip: "{{ item.gateway_ip | default(None) }}"
dns_nameservers: "{{ item.dns if item.dns is defined else omit }}"
enable_dhcp: yes
name: "{{ item.subnet }}"
network_name: "{{ item.network }}"
cidr: "{{ item.cidr }}"
allocation_pool_start: "{{ item.allocation_pool_start }}"
allocation_pool_end: "{{ item.allocation_pool_end }}"
host_routes: "{{ item.host_routes | default(omit) }}"
with_items:
- "{{ subnets }}"
tags: subnets
In my environment, I will have some subnets that will have gateway configured, some not. I would like to create a workaround to make it possible configuring gateway ip for some servers and for some of them not.
I have tried yet to configure it like this, but it will assign also for the ones that do not have the gateway_ip configured in the variable file a gateway. I have tried also the no_gateway_ip option, but for this one I didn't find the proper filter to get a gateway_ip when it is defined in the variable file.
Any way to trick this?
Found the way: no_gateway_ip should be involved, not gateway_ip.
- name: Create the subnets
os_subnet:
cloud: "{{ item.cloud }}"
state: present
validate_certs: no
no_gateway_ip: "{{ not (item.gateway_ip is defined) }}"
dns_nameservers: "{{ item.dns if item.dns is defined else omit }}"
enable_dhcp: yes
name: "{{ item.subnet }}"
network_name: "{{ item.network }}"
cidr: "{{ item.cidr }}"
allocation_pool_start: "{{ item.allocation_pool_start }}"
allocation_pool_end: "{{ item.allocation_pool_end }}"
host_routes: "{{ item.host_routes | default(omit) }}"
with_items:
- "{{ subnets }}"
tags: subnets

Resources