How do I map metadata to a variable? - ansible

I have an ansible playbook in which I need to pass 2 metadata elements to 2 different variables.
My relevent code in my yml is:
- debug:
var: result
- name: convert
set_fact:
var1: "{{ result | map(attribute='appname') }}"
var2: "{{ result | map(attribute='vipport') }}"
My metadata output looks like this:
"result": {
"changed": true,
"failed": false,
"meta": {
"appname": " testserver4",
"serverquerytype": "A",
"servicemonitor": "http-ecv",
"serviceport": 4433,
"vipmethod": "LEASTCONNECTION",
"vipport": 80,
"viptype": "HTTP"
}
I need to be able to create a variable of appname and vipport, the code I tried above does not work. Any idea what I am missing?

Below I put examples of how you can manipulate the result variable. Set_fact is redundant but I have added and example of taking the variable directly from the result variable.
- hosts: localhost
vars:
"result": {
"changed": true,
"failed": false,
"meta": {
"appname": " testserver4",
"serverquerytype": "A",
"servicemonitor": "http-ecv",
"serviceport": 4433,
"vipmethod": "LEASTCONNECTION",
"vipport": 80,
"viptype": "HTTP"
}
}
tasks:
- name: set var1 and var2 from result var
set_fact:
var1: "{{ result.meta.appname }}"
var2: "{{ result.meta.vipport }}"
- name: output of set_fact
debug:
msg:
- "{{ var1 }}"
- "{{ var2 }}"
- name: Output directly from result variable. Just an example that set_fact is not really needed.
debug:
msg:
- "{{ result.meta.appname }}"
- "{{ result.meta.vipport }}"
Output:
TASK [set var1 and var2 from result var] ***************************************************************************************************************************************************
ok: [localhost]
TASK [output of set_fact] ******************************************************************************************************************************************************************
ok: [localhost] => {
"msg": [
" testserver4",
"80"
]
}
TASK [Output directly from result variable. Just an example that set_fact is not really needed.] *******************************************************************************************
ok: [localhost] => {
"msg": [
" testserver4",
"80"
]
}

Related

Unique values from ansible output dict's

I have some servers with a lot of wordpress instances, who I ask them what versions they have.
- name: CONTADOR WP VERSIONES
shell: mycommand
register: wp_versions
- debug: msg: "{{ wp_versions.stdout_lines }}"
For example:
TASK [debug] *********************************************************************
ok: [server1] => {
"msg": [
"5.1.13"
]
}
ok: [server2] => {
"msg": [
"5.1.12",
"5.1.13"
]
}
ok: [server3] => {
"msg": [
"5.1.10",
"5.1.13",
]
}
I need to list a unique values like this:
"msg": [
"5.1.10",
"5.1.12",
"5.1.13",
]
I have tried all that i found but nothing works as I want.
Thanks
Use special variable ansible_play_hosts and extract the variables from the hostvars
- set_fact:
all_vers: "{{ ansible_play_hosts|
map('extract', hostvars, ['wp_versions', 'stdout_lines'])|
flatten|unique }}"
run_once: true
gives
all_vers:
- 5.1.13
- 5.1.12
- 5.1.10
You could do something like this:
- hosts: all
gather_facts: false
tasks:
- name: CONTADOR WP VERSIONES
shell: mycommand
register: wp_versions
- hosts: localhost
gather_facts: false
tasks:
# This tasks builds a flattened list of all the
# wp_versions.stdout_lines values collected from your hosts.
- name: Collect wp_versions information
set_fact:
all_wp_versions_pre: "{{ all_wp_versions_pre + hostvars[item].wp_versions.stdout_lines }}"
loop: "{{ groups.all }}"
vars:
all_wp_versions_pre: []
# Here we use the `unique` filter to produce a list of
# unique versions.
- name: Set all_wp_versions fact
set_fact:
all_wp_versions: "{{ all_wp_versions_pre|unique }}"
- debug:
var: all_wp_versions
Given you examples, this would produce the following output:
TASK [debug] ********************************************************************************************
ok: [localhost] => {
"all_wp_versions": [
"5.1.13",
"5.1.12",
"5.1.10"
]
}

Why is ansible creating a new host scoped list variables for each host?

I need to create a variable of ip addresses that are derived from the run state of all hosts. I expect to insert a new string value and have it appended to the list.
When I run the following ansible-playbook, it creates what looks to be a new list instance for each host instead of modifying the playbook level vars.
My understanding is the set_fact below should concatenate the lists and assign them back to the play scoped var ip_list_from_terraform. Why am I getting host scoped results?
---
- name: Bootstrap nodes
hosts: all
become: true
gather_facts: true
vars:
ip_list_from_terraform: ['Verify']
tasks:
- name: assign a list of all the physical network private IPs
set_fact:
ip_list_from_terraform: "{{ ip_list_from_terraform + [ item ] }}"
with_items: " {{ hostvars[inventory_hostname]['ansible_' + ansible_default_ipv4['interface']]['ipv4']['address'] }} "
register: ip_list_from_terraform_list
- name: Debug global var
debug:
var: ip_list_from_terraform
- name: Debug register result
debug:
var: ip_list_from_terraform_list
Expected:
ok: [shrimp-master-0] => {
"ip_list_from_terraform": [
"Verify",
"10.0.2.41",
"10.0.2.172",
"10.0.2.33",
"10.0.2.215",
"10.0.2.131",
"10.0.2.168",
"10.0.2.118"
]
}
Actual:
TASK [Debug global var] ********************************************************************************************************************************************************************************************************************
ok: [shrimp-master-0] => {
"ip_list_from_terraform": [
"Verify",
"10.0.2.12"
]
}
ok: [shrimp-master-1] => {
"ip_list_from_terraform": [
"Verify",
"10.0.2.33"
]
}
ok: [shrimp-master-2] => {
"ip_list_from_terraform": [
"Verify",
"10.0.2.215"
]
}
ok: [shrimp-worker-0-super-wallaby] => {
"ip_list_from_terraform": [
"Verify",
"10.0.2.131"
]
}
ok: [shrimp-gpu-worker-0-settled-wombat] => {
"ip_list_from_terraform": [
"Verify",
"10.0.2.151"
]
}
Let's simplify the case. Given the inventory
shell> cat hosts
host1 test_ip=10.0.2.41
host2 test_ip=10.0.2.172
host3 test_ip=10.0.2.33
the playbook
- hosts: host1,host2,host3
vars:
ip_list_from_terraform: ['Verify']
tasks:
- set_fact:
ip_list_from_terraform: "{{ ip_list_from_terraform + [ item ] }}"
with_items: "{{ hostvars[inventory_hostname]['test_ip'] }}"
- debug:
var: ip_list_from_terraform
gives
ok: [host2] =>
ip_list_from_terraform:
- Verify
- 10.0.2.172
ok: [host1] =>
ip_list_from_terraform:
- Verify
- 10.0.2.41
ok: [host3] =>
ip_list_from_terraform:
- Verify
- 10.0.2.33
As a side-note, with_items is needed because the argument is a string. loop would crash with the error 'Invalid data passed to ''loop'', it requires a list,.
Q: "set_fact should concatenate the lists"
A: Run once and loop ansible_play_hosts. For example
- set_fact:
ip_list_from_terraform: "{{ ip_list_from_terraform +
[hostvars[item]['test_ip']] }}"
loop: "{{ ansible_play_hosts }}"
run_once: true
gives
ip_list_from_terraform:
- Verify
- 10.0.2.41
- 10.0.2.172
- 10.0.2.33

Processing embedded lists inside dict variables in Ansible

I got the example code in Ansible:
- name: testing
hosts: localhost
vars:
svc:
https: ['tcp/443']
app_svc: ['tcp/5543', 'udp/5543', 'tcp/3100']
tasks:
- name: print
debug:
msg: port={{item.key}} value={{item.value}}
with_dict:
- "{{svc}}"
And this outputs to:
ok: [127.0.0.1] => (item=None) => {
"msg": "port=app_svc value=[u'tcp/5543', u'udp/5543', u'tcp/3100']"
}
ok: [127.0.0.1] => (item=None) => {
"msg": "port=https value=[u'tcp/443']"
}
What I would like to achieve is, when there is more than one element in list of values it would split like this:
- name=https, prot=tcp, port=443
- name=app_svc, prot=tcp, port=5543
- name=app_svc, prot=udp, port=5543
- name=app_svc, prot=tcp, port=3100
with_dict stanza only displays me a whole list and I couldn't find a way do do it differently. Is it possible to do it like that without reorganizing the var section? Thanks in advance for input.
To see the syntax errors run
# ansible-lint <YOUR-PLAYBOOK>
Correct syntax should be
- hosts: localhost
gather_facts: no
vars:
svc:
https: ['tcp/443']
app_svc: ['tcp/5543', 'udp/5543', 'tcp/3100']
tasks:
- name: print
debug:
msg: "port={{ item.key }} value={{ item.value }}"
with_dict: "{{ svc }}"
gives
"msg": "port=app_svc value=[u'tcp/5543', u'udp/5543', u'tcp/3100']"
"msg": "port=https value=[u'tcp/443']"
Change the loop
- name: print
debug:
msg: "name={{ item.0.key }},
prot={{ item.1.split('/')[0] }},
port={{ item.1.split('/')[1] }}"
loop: "{{ lookup('subelements', svc|dict2items, 'value') }}"
to get the format
"msg": "name=app_svc, prot=tcp, port=5543"
"msg": "name=app_svc, prot=udp, port=5543"
"msg": "name=app_svc, prot=tcp, port=3100"
"msg": "name=https, prot=tcp, port=443"
dict2items is available since version 2.6 . Without "dict2items" transform the data first. See below (not tested).
https:
- {key: 'https', value: ['tcp/443']}
- {key: 'app_svc', value: ['tcp/5543', 'udp/5543', 'tcp/3100']}

In Ansible, how can I show the variable name in a debug module?

Say I have a defaults/main.yml file that has
---
var1: my_value1
var2: my_value2
Now I want to print my vars in a debug
---
- debug: ??? what to put here ???
with_items:
- "{{ var1 }}"
- "{{ var2 }}"
So I show
The value of var1 is my_value1
The value of var2 is my_value2
var and msg only show the value, not the variable name? This seems so simple but I can't find in in the Ansible docs.
You can use with_dict to show var names but you have to define your variables differently :
---
vars:
var1:
my_value1
var2:
my_value2
And then you can do :
---
debug:
msg: "The value of {{item.key}} is {{item.value}}"
with_dict: "{{ vars }}"
This worked for me. It's not quite the message I wanted, but it at least shows me the item name and its value
- debug: var="{{item}}"
with_items:
- var1
- var2
and I get
TASK [python3 : debug] *********************************************************
ok: [localhost] => (item=var1) => {
"item": "var1",
"var1": "my_value1"
}
ok: [localhost] => (item=var2) => {
"var2": "my_value2",
"item": "var2"
}

resolve dictionary key or parameter variable in Ansible

In Ansible, if I try to use a variable as a parameter name, or a key name, it is never resolved. For example, if I have {{ some_var }}: true, or:
template: "{{ resolve_me_to_src }}": "some_src"
the variables will just be used literally and never resolve. My specific use case is using this with the ec2 module, where some of my tag names are stored as variables:
- name: Provision a set of instances
ec2:
group: "{{ aws_security_group }}"
instance_type: "{{ aws_instance_type }}"
image: "{{ aws_ami_id }}"
region: "{{ aws_region }}"
vpc_subnet_id: "{{ aws_vpc_subnet_id }}"
key_name: "{{ aws_key_name }}"
wait: true
count: "{{ num_machines }}"
instance_tags: { "{{ some_tag }}": "{{ some_value }}", "{{ other_tag }}": "{{ other_value }}" }
Is there any way around this? Can I mark that I want to force evaluation somehow?
Will this work for you?
(rc=0)$ cat training.yml
- hosts: localhost
tags: so5
gather_facts: False
vars: [
k1: 'key1',
k2: 'key2',
d1: "{
'{{k1}}': 'value1',
'{{k2}}': 'value2',
}",
]
tasks:
- debug: msg="{{item}}"
with_dict: "{{d1}}"
(rc=0)$ ansible-playbook training.yml -t so5
PLAY [localhost] ****************************************************************
PLAY [localhost] ****************************************************************
TASK: [debug msg="{{item}}"] **************************************************
ok: [localhost] => (item={'key': 'key2', 'value': 'value2'}) => {
"item": {
"key": "key2",
"value": "value2"
},
"msg": "{'value': 'value2', 'key': 'key2'}"
}
ok: [localhost] => (item={'key': 'key1', 'value': 'value1'}) => {
"item": {
"key": "key1",
"value": "value1"
},
"msg": "{'value': 'value1', 'key': 'key1'}"
}
PLAY RECAP ********************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0
(rc=0)$
Trick is to wrap dict declaration with double quotes. Ansible applies this undocumented (but consistant) and crappy translation (ansible's equivalent of shell variable expantion) to most (not all) YAML values (everything RHS of ':') in the playbook. It is some combination putting these strings through Jinja2-engine, Python-interpreter and ansible-engine in some unknown order.
Another option - you can try something like:
module_name: "{{ item.key }}={{ item.value }}"
with_items:
- { key: "option", value: "{{ any_value }}" }
Please note that everything is inline and I'm using an equal (=) and everything is wrapped with double quotes.

Resources