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

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 }}"

Related

Combine two ansible tasks but run only one based on condition and register result

Team,
I have a situation of running one task out of two based on condition when result and both are exact same but just that expression is different.
tasks
- name: Create partitions WHEN it is NVME device
parted:
device: "{{ item.0.device }}"
number: "{{ item.1.number }}"
part_start: "{{ item.1.start }}"
part_end: "{{ item.1.end }}"
state: present
loop: "{{ local_volume_mount_disks|subelements('partitions') }}"
when: '"{{item.0.device }}" is regex("nvme\w+")'
register: partitioned_device_live_info
- name: Create partitions WHEN it is SD* device
parted:
device: "{{ item.0.device }}"
number: "{{ item.1.number }}"
part_start: "{{ item.1.start }}"
part_end: "{{ item.1.end }}"
state: present
loop: "{{ local_volume_mount_disks|subelements('partitions') }}"
when: '"{{item.0.device }}" is regex("sd\w+")'
register: partitioned_device_live_info
But what is happening is the register variable partitioned_device_live_info is being replaced by result of its task result. So, I want to retain the results in registered variable PASS results. is there a way, I can join these two tasks such that register variable once is filled is not replaced when when is false?
something like Task1 when is true, register variable has its result, then task2 is checked but register variable is skipped and not replaced. because I need to continue with other tasks with that variable having certain value which i get only when when is TRUE.
Yes and no. Yes there is an ugly solution but also no this is expected behavior. Ugly solution below:
Use the set-fact on a when basis
- name: Create partitions WHEN it is NVME device
parted:
device: "{{ item.0.device }}"
number: "{{ item.1.number }}"
part_start: "{{ item.1.start }
part_end: "{{ item.1.end }}"
state: present
loop: "{{ local_volume_mount_disks|subelements('partitions') }}"
when: '"{{item.0.device }}" is regex("nvme\w+")'
register: tmp
- set_fact: partitioned_device_live_info ="{{tmp}}"
when: tmp.changed
- name: Create partitions WHEN it is SD* device
parted:
device: "{{ item.0.device }}"
number: "{{ item.1.number }}"
part_start: "{{ item.1.start }}"
part_end: "{{ item.1.end }}"
state: present
loop: "{{ local_volume_mount_disks|subelements('partitions') }}"
when: '"{{item.0.device }}" is regex("sd\w+")'
register: tmp
- set_fact: partitioned_device_live_info ="{{tmp}}"
when: tmp.changed

Can we have 2 with_items in ansible in a single task

Below is the condition
- name: Find the image
slurp:
src: "{{ IMAGE }}"
register: slurp_results
- name: Upload image
shell: |
skopeo copy -docker-archive:{{ item }}.tar docker://{{ URL }}/TESTIMAGE
with_items: "{{ (slurp_results.content|b64decode).splitlines() }}"
The above code works.
But I would need "TESTIMAGE" also to be replaced as {{ item }} like below.
skopeo copy -docker-archive:{{ item }}.tar docker://{{ URL }}/{{ item }}
How to define 2 with_items in a single shell task with 2 different slurp results
I believe you can by using the subelements module. Here is a link. Try going by this example:
- name: Setup MySQL users, given the mysql hosts and privs subkey lists
mysql_user:
name: "{{ item.0.name }}"
password: "{{ item.0.mysql.password }}"
host: "{{ item.1 }}"
priv: "{{ item.0.mysql.privs | join('/') }}"
with_subelements:
- "{{ users }}"
- mysql.hosts
Users is referred to as item.0 and hosts as item.1 and so on.

Registering and using multiple variables in Ansible

I'm trying to pop VM instances, put them into different host groups (say webservers and devops/admin machines) and install what is needed on them in one single playbook.
I don't know what IP addresses, for instance, GCP will give these instances, and so i am trying to capture them in a variable for use later on in the playbook. I can capture them fine by using "register" but using them is proving tricky. For instance if I do.
- name: création des adresses statiques
gcp_compute_address:
name: "{{ item }}"
state: present
region: "{{ region }}"
project: "{{ gcp_project }}"
auth_kind: "{{ gcp_cred_kind }}"
service_account_file: "{{ gcp_cred_file }}"
scopes:
- https://www.googleapis.com/auth/compute
loop:
- adresse-1
- adresse-2
- adresse-3
- adresse-4
- adresse-5
register: address
The best way i have figured out to use these variables later on is:
network_interfaces:
- network: "{{ network.name }}"
access_configs:
- name: 'External NAT'
type: 'ONE_TO_ONE_NAT'
nat_ip:
- "{{ address.results[0].address }}"
- "{{ address.results[1].address }}"
- "{{ address.results[2].address }}"
Which fails miserably.
Please help ? How can I use the range of addresses I have created ?
I am going nuts over this
It is possible to add_host to the group webservers and proceed with the next play
- add_host:
name: "{{ item }}"
groups: webservers
loop: "{{ address.results|json_query('[*].address') }}"
- debug:
msg: "{{ groups['webservers'] }}"
- hosts: webservers
tasks:
- name: Configure cluster
...
The tasks below split the hosts into two groups
- set_fact:
my_hosts: "{{ address.results|json_query('[*].address') }}"
- add_host:
name: "{{ item }}"
groups: webservers1
loop: "{{ my_hosts[0:(my_hosts|length / 2)|int] }}"
- add_host:
name: "{{ item }}"
groups: webservers2
loop: "{{ my_hosts[(my_hosts|length / 2)|int:my_hosts|length] }}"
- debug:
msg: "{{ groups['webservers1'] }}"
- debug:
msg: "{{ groups['webservers2'] }}"
There is also GCE Dynamic Inventory and other 100+ gcp modules. You might want to start with Google Cloud Platform Guide.
FWIW, Ansible 2 Cloud Automation Cookbook covers leading providers incl. GCP.

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

ansible 1.8 does not resolve item.key in set_facts

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)

Resources