How to get interpolated value of variable in Ansible / Jinja2 - ansible

I'm trying to define Ansible variables this way:
user:
name: First Last
nick: '{{ vars["user"]["name"] | regex_replace("\W", "_") }}'
email: '{{ vars["user"]["nick"] }}#example.com'
And the result email is: "{{ vars[\"user\"][\"name\"] | regex_replace(\"\\W\", \"_\") }}#example.com.
I also tried to set email like this: {{ lookup("vars", "user.nick") }}#example.com or {{ lookup("vars", "user")["nick"] }}#example.com, and it says An unhandled exception occurred while running the lookup plugin 'vars'.
Is there a way to get resulting variable values as:
user:
name: First Last
nick: First_Last
email: First_Last#example.com
?
ansible 2.9.10,
python version = 3.8.5

It's not possible cross-reference keys in a dictionary. It's necessary to declare the variables outside the dictionary. For example, the playbook
- hosts: localhost
vars:
my_name: First Last
my_nick: "{{ my_name | regex_replace('\\W', '_') }}"
user:
name: "{{ my_name }}"
nick: "{{ my_nick }}"
email: "{{ my_nick }}#example.com"
tasks:
- debug:
var: user
gives (abridged)
user:
email: First_Last#example.com
name: First Last
nick: First_Last
A more flexible option is to create the variables in the loop. For example, the playbook
- hosts: localhost
vars:
users:
"First Last":
domain: example.com
tasks:
- debug:
msg:
- "name: {{ name }}"
- "nick: {{ nick }}"
- "email: {{ email }}"
loop: "{{ users|dict2items }}"
vars:
name: "{{ item.key }}"
nick: "{{ item.key|regex_replace('\\W', '_') }}"
email: "{{ nick ~ '#' ~ item.value.domain }}"
gives (abridged)
msg:
- 'name: First Last'
- 'nick: First_Last'
- 'email: First_Last#example.com'

Related

Ansible variables question from output and filtered with json_query

I am trying to get the pertinent fields from a vmware module output, I am filtering with json, but the says my variable target_vm_name is undefined. how do I get a variable with only the matching criteria which is filtered by the ip address of the list of hosts I want to iterate through?
code is below:
- name: Loop thru hosts and get IPs
hosts: rpa_test
vars:
ip_addr:
"{{ lookup('dig', ansible_host) }}"
tasks:
- debug:
msg: "System {{ inventory_hostname }} has ip address of {{ ip_addr }}"
- name: get vm info based on ip
community.vmware.vmware_vm_info:
hostname: "{{ vcenter_hostname }}"
username: "{{ vault_vcenter_admin_name }}"
password: "{{ vault_vcenter_admin_password }}"
validate_certs: False
delegate_to: localhost
register: vm_info
vars:
target_vm_name: "{{ vm_info.virtual_machines | json_query(query) }}"
query: "[?ip_address=={{ ip_addr }}]"
- debug:
msg: "{{ target_vm_name.virtual_machines.guest_name }}"
vars is not at the right place inside the community.vmware.vmware_vm_info task ,
you should put it in the debug task:
tasks:
- debug:
msg: "System {{ inventory_hostname }} has ip address of {{ ip_addr }}"
- name: get vm info based on ip
community.vmware.vmware_vm_info:
hostname: "{{ vcenter_hostname }}"
username: "{{ vault_vcenter_admin_name }}"
password: "{{ vault_vcenter_admin_password }}"
validate_certs: False
delegate_to: localhost
register: vm_info
- debug:
msg: "{{ target_vm_name.virtual_machines.guest_name }}"
vars:
target_vm_name: "{{ vm_info.virtual_machines | json_query(query) }}"
query: "[?ip_address=={{ ip_addr }}]"

Ansible loop through nested inventory lists

I'm trying to loop through a nested ansible inventory looks like this:
inventory:
group_one:
- name: 'entry-one-a'
description: 'one-a'
group_two:
- name: 'entry-two-aa'
description: 'two-aa'
group_three:
- name: 'entry-three-aaa'
description: 'three-aaa'
- name: 'entry-three-aab'
description: 'three-aab'
I've tried it with the following loop, but without success:
- name: print vars
ansible.builtin.debug:
msg: '{{ item }}'
loop: '{{ inventory.group_one.group_two|subelements("group_three") }}'
Any good idea how to loop through the inventory?
Iterate the third loop in the included task, e.g.
shell> cat group_three.yml
- debug:
msg: "{{ item.0.name }} {{ item.1.name }} {{ item2.name }}"
loop: "{{ item.1.group_three }}"
loop_control:
loop_var: item2
- include_tasks: group_three.yml
with_subelements:
- "{{ inventory.group_one }}"
- group_two
gives
msg: entry-one-a entry-two-aa entry-three-aaa
msg: entry-one-a entry-two-aa entry-three-aab

ansible variable basing on hostname's suffix

I need to create a user on a system depending on it's function (test or production)
Test systems are named with suffix -tXX while prod ones are with -pXX (where XX is two digits numbering).
in variable file I have set:
auser: "{{ 'usertest' if {{ ansible_hostname|lower }} | regex_search('t[0-9]{2}$') else 'userprod' }}"
the error I get is during playbook run is:
fatal: [192.168.1.10]: FAILED! => {"msg": "An unhandled exception occurred while templating '{{ 'usertest' if {{ ansible_hostname|lower }} | regex_search('t[0-9]{2}$') else 'userprod' }}'. Error was a <class 'ansible.errors.AnsibleError'>, original message: template error while templating string: expected token ':', got '}'. String: {{ 'usertest' if {{ ansible_hostname|lower }} | regex_search('t[0-9]{2}$') else 'userprod' }}"}
Complete playbook is:
---
- hosts: testgrp
become: yes
vars:
- auser: "{{ 'usertest' if {{ ansible_hostname|lower }} | regex_search('t[0-9]{2}$') else 'userprod' }}"
tasks:
- name: Add groups
group:
name: "{{ item.name }}"
gid: "{{ item.gid }}"
state: present
loop:
- { name: 'group1', gid: '1101' }
- { name: 'group2', gid: '1102' }
- { name: 'group3', gid: '1103' }
- name: Add users
user:
name: "{{ item.name }}"
group: "{{ item.group }}"
groups: "{{ item.groups }}"
state: present
loop:
- { name: "{{ auser }}", group: 'group1', groups: 'group2,group3', uid: '1101' }
For example
- hosts: srv-t01,srv-p01
gather_facts: false
vars:
auser: "{{ inventory_hostname | lower is match('^.*-t[0-9]{2}$') |
ternary('usertest', 'userprod') }}"
tasks:
- debug:
var: auser
gives
ok: [srv-t01] =>
auser: usertest
ok: [srv-p01] =>
auser: userprod
A more robust solution is to select an index, e.g. the play below gives the same result
- hosts: srv-t01,srv-p01
gather_facts: false
vars:
auser_dict:
t: usertest
p: userprod
aindex: "{{ inventory_hostname | lower |
regex_replace('^.*-(.+)[0-9]{2}$', '\\1') }}"
auser: "{{ auser_dict[aindex] }}"
tasks:
- debug:
var: auser

Can ansible create a dict using the common values from a list and a dict?

In ansible, the vmware_guest_info module will give us a list of the tags on a vm, but does not include any information about those tags:
"tags": [
"10.16.3",
"dicky",
"develop"
],
The vmware_tag_info module gives us a dict withinfo on those tags, including description and Id, but NOT the tags name:
"10.16.3": {
"tag_category_id": "urn:vmomi:InventoryServiceCategory:6eb9d643-8fa3-42a1-8b50-78a1c6e99867:GLOBAL",
"tag_description": "10.16.3",
"tag_id": "urn:vmomi:InventoryServiceTag:ca46ab80-be91-4c3a-8f9f-019d163dd954:GLOBAL",
"tag_used_by": []
},
The vmware_category_info module gives us a list that includes ID and name of a tag.
"tag_category_info": [
{
"category_associable_types": [],
"category_cardinality": "SINGLE",
"category_description": "nodeVersion",
"category_id": "urn:vmomi:InventoryServiceCategory:6eb9d643-8fa3-42a1-8b50-78a1c6e99867:GLOBAL",
"category_name": "nodeVersion",
"category_used_by": []
},
]
So it seems I need to combine the output of three different lists to get the tag value, tag name and tag ID.
I really hope that someone has already done this. If not, can anyone shed some light on how to iterate over the output of vmware_tag_info and vmware_category_info, and find when tag_category_id matches category_id?
How about this?
---
- name: Example playbook
hosts: localhost
gather_facts: no
vars:
vcenter_hostname: change me
vcenter_username: administrator#vsphere.local
vcenter_password: change me
dc1: change me
vm_name: change me
tasks:
- name: Gather tags information
vmware_tag_info:
hostname: "{{ vcenter_hostname }}"
username: "{{ vcenter_username }}"
password: "{{ vcenter_password }}"
validate_certs: no
register: tags_info_result
- name: Gather tags category information
vmware_category_info:
hostname: "{{ vcenter_hostname }}"
username: "{{ vcenter_username }}"
password: "{{ vcenter_password }}"
validate_certs: no
register: tags_category_info_result
- name: Set tags_information variable
set_fact:
tags_information: >-
{{ tags_information | default({})
| combine({
item.key: tags_category_info_result.tag_category_info
| selectattr('category_id', '==', item.value.tag_category_id)
| list
| first
| combine(item.value)
| combine({'tag_name': item.key})
})
}}
with_dict: "{{ tags_info_result.tag_facts }}"
- name: Gather VM information
vmware_guest_info:
hostname: "{{ vcenter_hostname }}"
username: "{{ vcenter_username }}"
password: "{{ vcenter_password }}"
validate_certs: no
datacenter: "{{ dc1 }}"
name: "{{ vm_name }}"
tags: true
register: vm_info_result
- name: Display VM tags information
debug:
msg:
- "tags information about {{ vm_name }}"
- "{{ tags_information[item] }}"
loop: "{{ vm_info_result.instance.tags }}"
when:
- "'tags' in vm_info_result.instance"

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