Use variable with item in when condition in ansible task - ansible

I'm using the below playbook to capture the vmware datacenter information, which is working fine without any issues:
---
- hosts: localhost
vars_files: 1credentials.yml
tasks:
- name: Gather information about all datacenters
community.vmware.vmware_datacenter_info:
hostname: '{{ vcenter_hostname }}'
username: '{{ vcenter_username }}'
password: '{{ vcenter_password }}'
validate_certs: no
delegate_to: localhost
register: datacenter
- debug:
msg: "{{ item.name }}"
loop: "{{ datacenter.datacenter_info }}"
when:
- item.name is defined
- item.name == datacenter
below is the output:
PLAY [localhost] *******************************************************************************************************************************************************
TASK [Gathering Facts] *************************************************************************************************************************************************
ok: [localhost]
TASK [Gather information about all datacenters] ************************************************************************************************************************
ok: [localhost]
TASK [debug] ***********************************************************************************************************************************************************
skipping: [localhost] => (item={'name': 'Datacenter-Test', 'moid': 'datacenter-1247', 'config_status': 'gray', 'overall_status': 'gray'})
ok: [localhost] => (item={'name': 'opendc-rookie', 'moid': 'datacenter-2', 'config_status': 'gray', 'overall_status': 'gray'}) => {
"msg": "opendc-rookie"
}
PLAY RECAP *************************************************************************************************************************************************************
localhost : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
But when I try to use a var_prompt and ask for user input for the variable datacenter, as below:
---
- hosts: localhost
vars_files: 1credentials.yml
vars_prompt:
- name: datacenter
prompt: mention the datacenter name
private: no
tasks:
- name: Gather information about all datacenters
community.vmware.vmware_datacenter_info:
hostname: '{{ vcenter_hostname }}'
username: '{{ vcenter_username }}'
password: '{{ vcenter_password }}'
validate_certs: no
delegate_to: localhost
register: datacenter
- debug:
msg: "{{ item.name }}"
loop: "{{ datacenter.datacenter_info }}"
when:
- item.name is defined
- item.name == "datacenter"
It is skipping the debug task without substituting the datacenter variable's value which was the user's input in the when condition. Kindly suggest how can I incorporate the variable value with the when condition having item. Below is the output skipping the variable
mention the datacenter name: opendc-rookie
PLAY [localhost] *******************************************************************************************************************************************************
TASK [Gathering Facts] *************************************************************************************************************************************************
ok: [localhost]
TASK [Gather information about all datacenters] ************************************************************************************************************************
ok: [localhost]
TASK [debug] ***********************************************************************************************************************************************************
skipping: [localhost] => (item={'name': 'Datacenter-Test', 'moid': 'datacenter-1247', 'config_status': 'gray', 'overall_status': 'gray'})
skipping: [localhost] => (item={'name': 'opendc-rookie', 'moid': 'datacenter-2', 'config_status': 'gray', 'overall_status': 'gray'})
skipping: [localhost]
PLAY RECAP *************************************************************************************************************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0

i suggest you to rename your register value (same name than the prompt var)
- hosts: localhost
vars_files: 1credentials.yml
vars_prompt:
- name: datacenter
prompt: mention the datacenter name
private: no
tasks:
- name: Gather information about all datacenters
community.vmware.vmware_datacenter_info:
hostname: '{{ vcenter_hostname }}'
username: '{{ vcenter_username }}'
password: '{{ vcenter_password }}'
validate_certs: no
delegate_to: localhost
register: datacenterX
- debug:
msg: "{{ item.name }}"
loop: "{{ datacenterX.datacenter_info }}"
when:
- item.name is defined
- item.name == datacenter

In the trace we can see that the debug task is iterating over 2 dict items with the keys (or property) name = "Datacenter-Test" and name = "opendc-rookie".
Since you have a condition on item.name == "datacenter", the condition will only be true for items having the property `name = "datacenter". Which is not what you want to achieve.
how can I add the list values "Datacenter-Test" and "opendc-rookie" as variable in the when condition
Here are two options to filter the list of datacenters in order to retain the two selected names.
"simple condition": use a simple or in the when condition.
"list condition": filter directly the list to iterate over the items defined in the list wanted_datacenters. This option works with an input ansible-playbook loop.yml --extra-vars='{"wanted_datacenters": ["opendc-rookie", "Datacenter-Test"]}'
- hosts: localhost
vars:
datacenter:
- name: opendc-rookie
- name: Datacenter-Test
wanted_datacenters:
- opendc-rookie
- Datacenter-Test
tasks:
- name: "simple condition"
debug:
msg: "{{ item.name }}"
loop: "{{ datacenter }}"
when: item.name == "Datacenter-Test" or item.name == "opendc-rookie"
- name: "list condition"
debug:
msg: "{{ item.name }}"
loop: "{{ datacenter|selectattr('name', 'in', wanted_datacenters)|list }}"

Use json_query. This filter doesn't fail when an attribute is missing. For example,
shell> cat pb.yml
- hosts: localhost
vars:
datacenter:
datacenter_info:
- config_status: gray
moid: datacenter-1247
name: Datacenter-Test
overall_status: gray
- config_status: gray
moid: datacenter-2
name: opendc-rookie
overall_status: gray
tasks:
- debug:
msg: "{{ _item }}"
vars:
_item: "{{ datacenter.datacenter_info|json_query(_query) }}"
_query: '[?name == `{{ dc_name|d("") }}`]'
gives
shell> ansible-playbook pb.yml -e dc_name=Datacenter-Test
PLAY [localhost] *****************************************************************************
TASK [debug] *********************************************************************************
ok: [localhost] =>
msg:
- config_status: gray
moid: datacenter-1247
name: Datacenter-Test
overall_status: gray
shell> ansible-playbook pb.yml
PLAY [localhost] *****************************************************************************
TASK [debug] *********************************************************************************
ok: [localhost] =>
msg: []
You can iterate the list. For example,
- debug:
msg: "{{ item.name }}"
loop: "{{ datacenter.datacenter_info|json_query(_query) }}"
vars:
_query: '[?name == `{{ dc_name|d("") }}`]'
If you want to test the name is in a list create an intersect. For example,
shell> cat pb.yml
- hosts: localhost
vars:
datacenter:
datacenter_info:
- config_status: gray
moid: datacenter-1247
name: Datacenter-Test
overall_status: gray
- config_status: gray
moid: datacenter-2
name: opendc-rookie
overall_status: gray
names: "{{ datacenter.datacenter_info|json_query('[].name') }}"
dc_names: ''
names_selected: "{{ names|intersect(dc_names.split(',')) }}"
tasks:
- debug:
msg: "{{ item.name }}"
loop: "{{ datacenter.datacenter_info }}"
when: item.name|d('') in names_selected
gives
shell> ansible-playbook pb.yml -e dc_names=Datacenter-Test,opendc-rookie
PLAY [localhost] *****************************************************************************
TASK [debug] *********************************************************************************
ok: [localhost] => (item={'config_status': 'gray', 'moid': 'datacenter-1247', 'name': 'Datacenter-Test', 'overall_status': 'gray'}) =>
msg: Datacenter-Test
ok: [localhost] => (item={'config_status': 'gray', 'moid': 'datacenter-2', 'name': 'opendc-rookie', 'overall_status': 'gray'}) =>
msg: opendc-rookie
shell> ansible-playbook pb.yml -e dc_names=opendc-rookie
PLAY [localhost] *****************************************************************************
TASK [debug] *********************************************************************************
skipping: [localhost] => (item={'config_status': 'gray', 'moid': 'datacenter-1247', 'name': 'Datacenter-Test', 'overall_status': 'gray'})
ok: [localhost] => (item={'config_status': 'gray', 'moid': 'datacenter-2', 'name': 'opendc-rookie', 'overall_status': 'gray'}) =>
msg: opendc-rookie
shell> ansible-playbook
PLAY [localhost] *****************************************************************************
TASK [debug] *********************************************************************************
skipping: [localhost] => (item={'config_status': 'gray', 'moid': 'datacenter-1247', 'name': 'Datacenter-Test', 'overall_status': 'gray'})
skipping: [localhost] => (item={'config_status': 'gray', 'moid': 'datacenter-2', 'name': 'opendc-rookie', 'overall_status': 'gray'})
skipping: [localhost]

Related

Iterating list of dictionaries with dictionary

I'm having trouble grasping data manipulation in Ansible. Here's the setup:
- hosts: emirr01
gather_facts: no
vars:
setup:
- emirr01: { label: "label1" }
- emirr02: { label: "label2" }
- emirr03: { label: "label3" }
lookup: [ "emirr01", "emirr02"]
use_labels: [ ]
tasks:
- debug: msg="setup={{ setup }}"
- debug: msg="lookup={{ lookup }}"
- debug: msg="item0={{ item.0 }} item1={{ item.1 }}"
when: inventory_hostname == item.1
with_nested:
- "{{ setup }}"
- "{{ lookup }}"
- set_fact:
use_labels: "{{ use_labels + [ item.1.label ] }}"
when: item.0 == item.1.label
with_nested:
- "{{ setup }}"
- "{{ lookup }}"
- debug: msg="use_labels={{ use_labels }}"
What I need is to set a fact use_labels which is a list of labels as defined in setup list for each host found in lookup list. What I expect is a list like this:
[ "label1", "label2" ]
My problem is not being able to "reach" label in a list I get in item.1 which is (example):
item1=[{'emirr02': {'label': 'label2'}}
Here's is the error:
$ ansible-playbook test.yml -l emirr01
PLAY [emirr01] ****************************************************************************************************************************************************************************************************************************************************************************************************************************
TASK [debug] ******************************************************************************************************************************************************************************************************************************************************************************************************************************
ok: [emirr01] =>
msg: 'setup=[{''emirr01'': {''label'': ''label1''}}, {''emirr02'': {''label'': ''label2''}}, {''emirr03'': {''label'': ''label3''}}]'
TASK [debug] ******************************************************************************************************************************************************************************************************************************************************************************************************************************
ok: [emirr01] =>
msg: lookup=['emirr01', 'emirr02']
TASK [debug] ******************************************************************************************************************************************************************************************************************************************************************************************************************************
ok: [emirr01] => (item=[{'emirr01': {'label': 'label1'}}, 'emirr01']) =>
msg: 'item0={''emirr01'': {''label'': ''label1''}} item1=emirr01'
skipping: [emirr01] => (item=[{'emirr01': {'label': 'label1'}}, 'emirr02'])
ok: [emirr01] => (item=[{'emirr02': {'label': 'label2'}}, 'emirr01']) =>
msg: 'item0={''emirr02'': {''label'': ''label2''}} item1=emirr01'
skipping: [emirr01] => (item=[{'emirr02': {'label': 'label2'}}, 'emirr02'])
ok: [emirr01] => (item=[{'emirr03': {'label': 'label3'}}, 'emirr01']) =>
msg: 'item0={''emirr03'': {''label'': ''label3''}} item1=emirr01'
skipping: [emirr01] => (item=[{'emirr03': {'label': 'label3'}}, 'emirr02'])
TASK [set_fact] ***************************************************************************************************************************************************************************************************************************************************************************************************************************
fatal: [emirr01]: FAILED! =>
msg: |-
The conditional check 'item.0 == item.1.label' failed. The error was: error while evaluating conditional (item.0 == item.1.label): 'ansible.utils.unsafe_proxy.AnsibleUnsafeText object' has no attribute 'label'
The error appears to be in '/home/ansible/test.yml': line 21, column 7, but may
be elsewhere in the file depending on the exact syntax problem.
The offending line appears to be:
- set_fact:
^ here
It all boils down to "digging" for a value from a dictionary in a list. Does anyone have an idea of how to reach item.1.label? It's a little bit frustrating since this is trivial in Python or other programming languages but in Ansible... Or maybe my mistake is to use a wrong kind of with_* loop for this purpose?
Q: "Expect a list like this [label1, label2]"
setup:
- emirr01: { label: "label1" }
- emirr02: { label: "label2" }
- emirr03: { label: "label3" }
lookup: [ "emirr01", "emirr02"]
A: This is a typical example of a wrong data structure for this use-case. A list of dictionaries can't be used in mapping the filter extract. Moreover, the keys in the dictionaries don't have to be unique, because they are hidden in the items of the list. The solution is simple with the data in the dictionary
setup:
emirr01: { label: "label1" }
emirr02: { label: "label2" }
emirr03: { label: "label3" }
- set_fact:
use_labels: "{{ lookup|map('extract', setup, 'label')|list }}"
gives
use_labels:
- label1
- label2
One of the options is creating a dictionary from the list first. For example
- set_fact:
setup2: "{{ setup2|default({})|combine(item) }}"
loop: "{{ setup }}"
Then the dictionary setup2 can be used to get the same result
It's possible to filter the list directly. For example, the task below gives the same result too
- set_fact:
use_labels: "{{ setup|map('dict2items')|map('first')|
selectattr('key', 'in', lookup )|
map(attribute='value.label')|
list }}"
Your issue with the with_nested is actually coming from how those "two-lists" loops are working.
When you are using loops like with_nested, the item.0 is the item of the first list, while the item.1 is the item of the second list.
Because lookup is a list and not a list of dictionaries like
lookup:
- label: "emirr01"
- label: "emirr02"
You can use the value of item.1 right away.
So the way to have you issue fixed is to use it this way:
- set_fact:
use_labels: "{{ use_labels + [ item.0[item.1].label ] }}"
when: item.0[item.1].label is defined
with_nested:
- "{{ setup }}"
- "{{ lookup }}"
Here is a playbook demonstrating this:
- hosts: localhost
gather_facts: no
tasks:
- set_fact:
use_labels: "{{ use_labels | default([]) + [ item.0[item.1].label ] }}"
when: item.0[item.1].label is defined
with_nested:
- "{{ setup }}"
- "{{ lookup }}"
vars:
setup:
- emirr01:
label: "label1"
- emirr02:
label: "label2"
- emirr03:
label: "label3"
lookup:
- "emirr01"
- "emirr02"
- debug:
msg: "{{ use_labels }}"
Wich gives:
PLAY [localhost] ***************************************************************************************************
TASK [set_fact] ****************************************************************************************************
ok: [localhost] => (item=[{'emirr01': {'label': 'label1'}}, 'emirr01'])
skipping: [localhost] => (item=[{'emirr01': {'label': 'label1'}}, 'emirr02'])
skipping: [localhost] => (item=[{'emirr02': {'label': 'label2'}}, 'emirr01'])
ok: [localhost] => (item=[{'emirr02': {'label': 'label2'}}, 'emirr02'])
skipping: [localhost] => (item=[{'emirr03': {'label': 'label3'}}, 'emirr01'])
skipping: [localhost] => (item=[{'emirr03': {'label': 'label3'}}, 'emirr02'])
TASK [debug] *******************************************************************************************************
ok: [localhost] => {
"msg": [
"label1",
"label2"
]
}
PLAY RECAP *********************************************************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

Ansible loop giving warning found a duplicate dict key (when). Using last defined value only

I am trying to iterate over an array and assign the value to variables hooks_enabled, workflow_artifact_id, workflow_version, one by one in every iteration and perform a specific task (currently debug, later change to Helm install command).
Code:
---
- name: Executing Ansible Playbook
hosts: localhost
become: yes
become_user: someuser
pre_tasks:
- include_vars: global_vars.yaml
- name: Print some debug information
set_fact:
all_vars: |
Content of vars
--------------------------------
{{ vars | to_nice_json }}
tasks:
- name: Iterate over an array
set_fact:
hooks_enabled: '{{ array_item1_hooks_enabled }}'
workflow_artifact_id: '{{ array_item1_workflow_artifact_id }}'
workflow_version: '{{ array_item1_workflow_version }}'
when: "item == 'array_item1'"
set_fact:
hooks_enabled: '{{ array_item2_hooks_enabled }}'
workflow_artifact_id: '{{ array_item2_workflow_artifact_id }}'
workflow_version: '{{ array_item2_workflow_version }}'
when: "item == 'array_item2'"
with_items: "{{ array}}"
# Change debug with helm install command
- debug:
msg: " id= '{{ workflow_artifact_id }}'"
The issue I am facing is, only the last when is considered and others are skipped
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'
[WARNING]: While constructing a mapping from /c/ansible-test/second.yaml, line 16, column 7, found a duplicate dict key (set_fact). Using last defined value only.
[WARNING]: While constructing a mapping from /c/ansible-test/second.yaml, line 16, column 7, found a duplicate dict key (when). Using last defined value only.
PLAY [Executing Ansible Playbook] *********************************************************************************************************************************************************************************
TASK [Gathering Facts] ********************************************************************************************************************************************************************************************
ok: [localhost]
TASK [include_vars] ***********************************************************************************************************************************************************************************************
ok: [localhost]
TASK [Print some debug information] *******************************************************************************************************************************************************************************
ok: [localhost]
TASK [Iterate over an array] **************************************************************************************************************************************************************************************
skipping: [localhost] => (item=array_item1)
ok: [localhost] => (item=array_item2)
skipping: [localhost] => (item=array_item3)
skipping: [localhost] => (item=array_item4)
skipping: [localhost] => (item=array_item5)
TASK [debug] ******************************************************************************************************************************************************************************************************
ok: [localhost] => {
"msg": " id= 'algorithm-Workflow'"
}
PLAY RECAP ********************************************************************************************************************************************************************************************************
localhost : ok=5 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
How do I modify the block to enable all the when statement execute and later use helm install command to take the variables one by one.
I would go with a dynamic variable construction using the vars lookup.
Something along the lines of:
- set_fact:
hooks_enabled: "{{ lookup('vars', item ~ '_hooks_enabled') }}"
workflow_artifact_id: "{{ lookup('vars', item ~ '_workflow_artifact_id') }}"
workflow_version: "{{ lookup('vars', item ~ '_workflow_version') }}"
when: "item in ['array_item1', 'array_item2']"
with_items: "{{ array }}"

Iterating via nested loops

The packages.yml file defined as:
---
- packages:
- name: Some description 1,
packageList:
- package1,
- package2,
- package3
- name: Some description 2,
package: package4
The first item contains a field packageList, the 2nd item does not have it, but only package field.
Playbook:
---
- hosts: all
become: yes
vars_files:
- packages.yml
How to iterate via all packageList items of the packages list only if this packageList is defined for an item.
Here is how I can iterate through items which contain package field:
- name: iteration
debug:
msg: "name: {{ item.package }}"
when: item.package is defined
with_items: "{{ packages }}"
As noted in my comment, if you simply want to install multiple yum/apt packages, it is usually more efficient to simply pass the list to the apt/yum/package module. As the docs state:
"When used with a loop: each package will be processed individually, it is much more efficient to pass the list directly to the name option."
However, if you really need a loop, here is a possible solution:
playbook.yml:
---
- hosts: all
gather_facts: false
vars_files:
- packages.yml
tasks:
- name: iteration over single items
debug:
msg: "name: {{ item.package }}"
when: item.package is defined
with_items: "{{ packages }}"
- name: iteration over lists
debug:
msg: "name: {{ item.packageList }}"
when: item.packageList is defined
with_items: "{{ packages }}"
- name: Do something with individual packages in the list
include_tasks: process_list.yml
vars:
mylist: "{{outer.packageList}}"
when: outer.packageList is defined
loop: "{{ packages }}"
loop_control:
loop_var: outer
process_list.yml:
- name: See what we have received
debug:
var: item
loop: "{{mylist}}"
result:
PLAY [all] *******************************************************************************************************************************
TASK [iteration over single items] *******************************************************************************************************
skipping: [localhost] => (item={u'packageList': [u'package1,', u'package2,', u'package3'], u'name': u'Some description 1,'})
ok: [localhost] => (item={u'name': u'Some description 2,', u'package': u'package4'}) => {
"msg": "name: package4"
}
TASK [iteration over lists] **************************************************************************************************************
ok: [localhost] => (item={u'packageList': [u'package1,', u'package2,', u'package3'], u'name': u'Some description 1,'}) => {
"msg": "name: [u'package1,', u'package2,', u'package3']"
}
skipping: [localhost] => (item={u'name': u'Some description 2,', u'package': u'package4'})
TASK [Do something with individual packages in the list] *********************************************************************************
skipping: [localhost] => (item={u'name': u'Some description 2,', u'package': u'package4'})
included: /root/tmp/process_list.yml for localhost
TASK [See what we have received] *********************************************************************************************************
ok: [localhost] => (item=package1,) => {
"ansible_loop_var": "item",
"item": "package1,"
}
ok: [localhost] => (item=package2,) => {
"ansible_loop_var": "item",
"item": "package2,"
}
ok: [localhost] => (item=package3) => {
"ansible_loop_var": "item",
"item": "package3"
}
PLAY RECAP *******************************************************************************************************************************
localhost : ok=4 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
The loop_control/loop_var part is used because otherwise both the outer and inner loop will use {{item}} as the loop variable - and this will cause... interesting results :)
You can define a default value with an empty list for the cases, where the packageList is undefined.
{{ item.packageList | default ([]) }}
If the packageList is undefined, the job iterates over an empty list, which means, it does not do anything.
You can use default, as #ceving mentioned:
---
- hosts: localhost
connection: local
vars_files:
- packages.yml
tasks:
- name: iteration
debug:
msg: "name: {{ item.packageList | default([item.package]) }}"
with_items: "{{ packages }}"
If packageList exists, it will use that, else package put into a single-element array to match the form of packageList:
PLAY [localhost] **********************************************************************************************
TASK [Gathering Facts] ****************************************************************************************
ok: [localhost]
TASK [iteration] **********************************************************************************************
ok: [localhost] => (item=None) => {
"msg": "name: [u'package1,', u'package2,', u'package3']"
}
ok: [localhost] => (item=None) => {
"msg": "name: [u'package4']"
}
PLAY RECAP ****************************************************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0

How can I traverse nested lists in Ansible?

I have a list of devices, each of which has a varying list of attributes that must be created on the devices, one at a time. Is there a way to build a nested set of loops to do this? The with_nested construct applies every attribute to every device; I need only a single attribute per device per call.
This playbook demonstrates what I have tried (Ansible 2.7.1, Python 2.7.1):
- name: Test nested list traversal
hosts: localhost
connection: local
vars:
Stuff:
- Name: DeviceA
Info: AInfo
Values:
- ValueA1
- ValueA2
- Name: DeviceB
Info: BInfo
Values:
- ValueB1
- ValueB2
- ValueB3
tasks:
- name: Loop test
debug:
msg: "{{ item.Name }},{{ item.Info }}, {{ item.Values }}"
with_items:
- "{{ Stuff }}"
What I get: (One call per device, containing all attributes at once)
ok: [localhost] => (item={u'Info': u'AInfo', u'Values': [u'ValueA1', u'ValueA2'], u'Name': u'DeviceA'}) =>
msg: DeviceA,AInfo, [u'ValueA1', u'ValueA2']
ok: [localhost] => (item={u'Info': u'BInfo', u'Values': [u'ValueB1', u'ValueB2', u'ValueB3'], u'Name': u'DeviceB'}) =>
msg: DeviceB,BInfo, [u'ValueB1', u'ValueB2', u'ValueB3']
What I want (each msg represents a separate operation to be performed on the device with just that one attribute)
msg: DeviceA, AInfo, ValueA1
msg: DeviceA, AInfo, ValueA2
msg: DeviceB, BInfo, ValueB1
msg: DeviceB, BInfo, ValueB2
msg: DeviceB, BInfo, ValueB3
You can get what you want using the subelements filter:
---
- hosts: localhost
gather_facts: false
vars:
Stuff:
- Name: DeviceA
Info: AInfo
Values:
- ValueA1
- ValueA2
- Name: DeviceB
Info: BInfo
Values:
- ValueB1
- ValueB2
- ValueB3
tasks:
- debug:
msg: "{{ item.0.Name }}, {{ item.0.Info }}, {{ item.1 }}"
loop: "{{ Stuff|subelements('Values') }}"
loop_control:
label: "{{ item.0.Name }}"
Running the above playbook gets you:
PLAY [localhost] ******************************************************************************************************************************************************************************
TASK [debug] **********************************************************************************************************************************************************************************
ok: [localhost] => (item=DeviceA) => {
"msg": "DeviceA, AInfo, ValueA1"
}
ok: [localhost] => (item=DeviceA) => {
"msg": "DeviceA, AInfo, ValueA2"
}
ok: [localhost] => (item=DeviceB) => {
"msg": "DeviceB, BInfo, ValueB1"
}
ok: [localhost] => (item=DeviceB) => {
"msg": "DeviceB, BInfo, ValueB2"
}
ok: [localhost] => (item=DeviceB) => {
"msg": "DeviceB, BInfo, ValueB3"
}
PLAY RECAP ************************************************************************************************************************************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0

How to access a programmatically constructed ansible variable?

I have constructed an ansible variable using two other defined ansible variables. The constructed variable is defined in the vars/main.yml and I want to access the defined value in vars/main.yml.
vars/main.yml
---
var1_var2: "some value"
Now, I construct the variable
---
- name: Construct and get the value
hosts: localhost
tasks:
- include_vars: "vars/main.yml"
- set_fact:
variable1: "var1"
variable2: "var2"
- set_fact:
final_variable: "{{ variable1 }}_{{ variable2 }}"
- set_fact:
ultimate_variable: "{{ final_variable }}"
If I run the playbook with -vvv flag, I can see that ultimate_variable sets to var1_var2 while I want to get the value defined in the vars/main.yml
i.e., some value
TASK [set_fact]
ok: [localhost] => {
"ansible_facts": {
"variable1": "var1",
"variable2": "var2"
},
"changed": false,
"failed": false
}
TASK [set_fact] task path: /home/ubuntu/test.yml:78
ok: [localhost] => {
"ansible_facts": {
"final_variable": "var1_var2"
},
"changed": false,
"failed": false
}
TASK [set_fact]
ok: [localhost] => {
"ansible_facts": {
"ultimate_variable": "var1_var2"
},
"changed": false,
"failed": false
}
updated answer:
use the lookup plugin to do the double replacement:
ultimate_variable: "{{ lookup('vars', '{{final_variable}}') }}"
playbook:
- include_vars: "vars/main.yml"
- set_fact:
variable1: "var1"
variable2: "var2"
- set_fact:
final_variable: "{{ variable1 }}_{{ variable2 }}"
- set_fact:
ultimate_variable: "{{ lookup('vars', '{{final_variable}}') }}"
- debug:
var: ultimate_variable
output:
PLAY [localhost] ****************************************************************************************************************************************************************************************************
TASK [include_vars] *************************************************************************************************************************************************************************************************
ok: [localhost]
TASK [set_fact] *****************************************************************************************************************************************************************************************************
ok: [localhost]
TASK [set_fact] *****************************************************************************************************************************************************************************************************
ok: [localhost]
TASK [set_fact] *****************************************************************************************************************************************************************************************************
ok: [localhost]
TASK [debug] ********************************************************************************************************************************************************************************************************
ok: [localhost] => {
"ultimate_variable": "some value"
}
PLAY RECAP **********************************************************************************************************************************************************************************************************
localhost : ok=5 changed=0 unreachable=0 failed=0
hope it helps.
You could also skip the intermediary variable definition:
- include_vars: "vars/main.yml"
- set_fact:
variable1: "var1"
variable2: "var2"
- set_fact:
ultimate_variable: "{{ lookup('vars', variable1 ~ '_' ~ variable2) }}"
- debug:
var: ultimate_variable

Resources