Parsing a list of strings in ansible - regex

I have this list:
ok: [localhost] => {
"ansible_facts": {
"servs": [
"Is active: Yes\nHost Name: northrend\n Ip Address: 192.168.1.12",
"Is active: Yes\nHost Name: pandaria \n IP Address: 192.168.1.30"
]
},
I've been trying to parse this list to extract the value of "Host name" but I just keep on getting the following error:
fatal: [localhost]: FAILED! => {
"msg": "Unexpected templating type error occurred on ({{ servs | regex_search('Host Name: (.)+\n'),multiline=True) }}): expected string or buffer"
}
This is what I tried:
- set_fact:
output: {{ servs | regex_search('Host Name: (.)+\n'), multiline=True) }}
How do you properly parse the contents of this list?

The list servs is nearly valid YAML. Only the indentation of the attribute 'Ip Address' is wrong. One space shifted to the right. If you manage to get rid of this space, e.g.
servs:
- "Is active: Yes\nHost Name: northrend\nIp Address: 192.168.1.12"
- "Is active: Yes\nHost Name: pandaria \nIP Address: 192.168.1.30"
the situation would be trivial. Simply convert the list of strings to a list of dictionaries, e.g.
- set_fact:
servs_list: "{{ servs_list|d([]) + [item|from_yaml] }}"
loop: "{{ servs }}"
gives
servs_list:
- Host Name: northrend
Ip Address: 192.168.1.12
Is active: true
- Host Name: pandaria
IP Address: 192.168.1.30
Is active: true
If you can't fix the indentation parse the data on your own. The task below gives the same result
- set_fact:
servs_list: "{{ servs_list|d([]) + [item|
regex_replace(regex, replace)|
from_yaml] }}"
loop: "{{ servs }}"
vars:
regex: '\n '
replace: '\n'
Get the output
- set_fact:
output: "{{ servs_list|map(attribute='Host Name')|list }}"
gives
output:
- northrend
- Pandaria

You have multiple errors here:
regex_search works on strings, not lists. So you need to apply it to each list element.
Your regex creates one group for each character in your hostname. The group should be (.+).
You are returning the whole match (including Host Name:) and not just the name itself. You need to specify which matching group to return.
I guess the list is from the output of some script or in some file. It would be a lot better to have it as json or yaml and then use from_json or from_yaml.
If you want to keep it like that, you need to do this:
- hosts: all
vars:
servs:
- |
Is active: Yes
Host Name: northrend
Ip Address: 192.168.1.12
- |
Is active: Yes
Host Name: pandaria
IP Address: 192.168.1.30
tasks:
- debug:
msg: "{{ servs }}"
- set_fact:
output: "{{ servs | map('regex_search', 'Host Name: (.+)\n', '\\1') | list }}"
- debug:
msg: "{{ output }}"
This is the output:
TASK [debug] ************
ok: [local] => {
"msg": [
"Is active: Yes\nHost Name: northrend\nIp Address: 192.168.1.12\n",
"Is active: Yes\nHost Name: pandaria\nIP Address: 192.168.1.30\n"
]
}
TASK [set_fact] ***************
ok: [local]
TASK [debug] ************
ok: [local] => {
"msg": [
[
"northrend"
],
[
"pandaria"
]
]
}

Related

Ansible returns 'AnibleUnicode' instead of JSON, unable to filter

I'm unable to filter the following output with json_query, even though it works on a jmespath validator website.
Let me explain in detail with the code.
type_debug returns AnsibleUnicode for members value, so when I tried to use json_query to filter output it returned null. I need to get value from key ip, but I don't know how to do it in a good way.
---
- hosts: localhost
gather_facts: false
vars:
- RPN:
stdout: >-
"members": [
{
"id": 0,
"ip": "x.x.x.x",
"owner": "buzut",
"private_ip": "10.91.154.39",
"speed": 100,
"status": "active"
}
]
tasks:
- debug:
msg: "{{ RPN.stdout | type_debug }}"
From your question I understand that you like to gather all member with a json_query. To do so I've corrected the JSON slightly.
---
- hosts: localhost
become: false
gather_facts: false
vars:
RPN:
stdout: >-
{
"members": [{
"id": 0,
"ip": "x.x.x.x",
"owner": "buzut",
"private_ip": "10.91.154.39",
"speed": 100,
"status": "active"
}]
}
tasks:
- name: Show type(s)
debug:
msg:
- "{{ RPN.stdout | type_debug }}"
- "{{ RPN.stdout | from_json | type_debug }}"
- "{{ RPN.stdout | to_json | type_debug }}"
- name: Show members
debug:
msg: "{{ RPN.stdout | from_json | json_query('members') }}"
resulting into an output of
TASK [Show type(s)] *********
ok: [localhost] =>
msg:
- AnsibleUnicode
- dict
- str
TASK [Show members] *********
ok: [localhost] =>
msg:
- id: 0
ip: x.x.x.x
owner: buzut
private_ip: 10.91.154.39
speed: 100
status: active
I need to get value from key ip
- name: Show value of key 'ip'
debug:
msg: "{{ RPN.stdout | from_json | json_query('members[*].ip') }}"
resulting into an output of
TASK [Show value of key 'ip'] *********
ok: [localhost] =>
msg:
- x.x.x.x
Further Documentation
Selecting JSON data: JSON queries

How do I add to my existing Ansible Dictionary by looking something up from another dictionary?

Been at this about a week and I think it's time to post this. I'm pretty decent at ansible but clearly terrible at dictionaries.
How do I add a sub-key to a dictionary using update_list?
Alright so I have two dictionaries, one is the output of a command to pull AD Creds, the dictionary looks like this called userresult:
{
"objects": [
{
"Name" : "Administrator"
"objectSid": {
"Name" : "DOMAIN\\Administrator"
"Sid": "S-1-5-21"
}
},
{
"Name" : "Guest"
"objectSid": {
"Name" : "DOMAIN\\Guest"
"Sid": "S-1-5-22"
}
]
}
Now, I've got a dictionary that has the names and info I want to lookup (inputdictionary.yml):
dictionary:
users:
Nonexistentuser:
old_sid: 1-1-1-1
domain: nonexistentdomain
Administrator:
old_sid: 2-2-2-2
domain: DOMAIN
What I've tried:
---
- name: Update dictionary
hosts: myhosts
gather_facts: no
tasks:
- name: Get SID for user - you can ignore this part it just gets userresult
community.windows.win_domain_object_info:
filter: ObjectClass -eq 'user' -and objectCategory -eq 'Person'
properties:
- objectSid
register: userresult
- name: ready yaml dictionary
delegate_to: localhost
slurp:
path: '/home/inputdictionary.yml'
register: SourceInfo
- name: extract dictionary
set_fact:
myinfo: "{{SourceInfo['content'}] | b64decode | from_yaml ))"
- name: build a list of updates for dictionary
set_fact:
update_list: "{{ update list + update }}"
loop: "{{ myinfo.dictionary.users | dict2items }}"
loop_control:
index_var: idx
vars:
update_list: []
update:
- path: myinfo.dictonary.users[{{idx}}].new_sid
value: "TEST"
- debug:
var: update_list
Expected output:
dictionary:
users:
Nonexistentuser:
old_sid: 1-1-1-1
domain: nonexistentdomain
new_sid: TEST
Administrator:
old_sid: 2-2-2-2
domain: DOMAIN
new_sid: TEST
Current output:
TASK [build a list of updates for dictinoary]
FAILED! -> {
"msg": "template error while templating string: expected token 'end of print statement', got 'list. String: {{update list + update}}"
}
Test needs to be replaced with the new SID eventually, but I just want this step to work first..
Been trying to follow this but no avail: https://docs.ansible.com/ansible/latest/collections/ansible/utils/update_fact_module.html
Any help would be appreciated while I continue to try to fix this..
TEST needs to be replaced with the new SID eventually, but I just want this step to work first
- name: "make this working"
hosts: localhost
vars:
dictionary:
users:
Nonexistentuser:
old_sid: 1-1-1-1
domain: nonexistentdomain
Administrator:
old_sid: 2-2-2-2
domain: DOMAIN
tasks:
- name: build a list of updates for dictionary
set_fact:
update_list: "{{ update_list | d({}) | combine({ item.key: _v}) }}"
loop: "{{ dictionary.users | dict2items }}"
vars:
_k: "{{ item.key }}"
_v: "{{ item.value | combine({'new_sid': newsid}) }}"
newsid: "TEST"
- debug:
msg: '{{ update_list }}'
result:
ok: [localhost] => {
"msg": {
"Administrator": {
"domain": "DOMAIN",
"new_sid": "TEST",
"old_sid": "2-2-2-2"
},
"Nonexistentuser": {
"domain": "nonexistentdomain",
"new_sid": "TEST",
"old_sid": "1-1-1-1"
}
}
}

Access yaml list of dictionaries file with ansible

So I am trying take values from file, let's call it "test.yaml"
file looks like this (sorry for long output, but it is the shortest cut containing all patterns and structure):
---
results:
- failed: false
item: XXX.XX.XX.XX
invocation:
module_args:
validate_certs: false
vm_type: vm
show_tag: false
username: DOMAIN\domain-user
proxy_host:
proxy_port:
show_attribute: false
password: VALUE_SPECIFIED_IN_NO_LOG_PARAMETER
port: XXX
folder:
hostname: XXX.XX.XX.XX
changed: false
virtual_machines:
- ip_address: XXX.XX.XX.XX
mac_address:
- XX:XX:XX:aa:XX:XX
uuid: XXXX-XX-XX-XXXX-XXXXX
guest_fullname: Red Hat Enterprise Linux X (XX-bit)
moid: vm-XXX
folder: "/DOMAIN-INTERXION/vm"
cluster:
attributes: {}
power_state: poweredOn
esxi_hostname: esx.hostname
tags: []
guest_name: VMnameXX
vm_network:
XX:XX:XX:aa:XX:XX:
ipv6:
- XX::XXX:XX:XXXX
ipv4:
- XXX.XX.XX.XX
I would like, for example to have something like:
results.invocation.virtual_machines.ip_address
results.invocation.module_args.user_name
I tried all kind of stuff but it doesn't work :)
last attempt is this:
---
- name: demo how register works
hosts: localhost
tasks:
- name: Include all .json and .jsn files in vars/all and all nested directories (2.3)
include_vars:
file: test.yml
name: vm
- name: debug
debug:
msg: "{{ item.0.item }}"
with_subelements:
- "{{ vm.results }}"
- virtual_machines
register: subelement
following your structure and after fixing some errors:
results.invocation.virtual_machines.ip_address is results[0].virtual_machines[0].ip_address
and
results.invocation.module_args.user_name is results[0].invocation.module_args.username
(results and virtual_machines are arrays, write results[0] or results.0 is same)
so a sample of playbook doing job:
- name: vartest
hosts: localhost
tasks:
- name: Include all .json and .jsn files in vars/all and all nested directories (2.3)
include_vars:
file: test.yml
name: vm
- name: ip
set_fact:
ipadress: "{{ vm.results[0].virtual_machines[0].ip_address }}"
- name: username
set_fact:
username: "{{ vm.results[0].invocation.module_args.username }}"
- name: display
debug:
msg: "ip: {{ ipadress }} and username: {{ username }}"
result:
ok: [localhost] =>
msg: 'ip: XXX.XX.XX.XX and username: DOMAIN\domain-user'

vmware_tag_manager, list to string trouble

Playbook below seems to be having trouble running thru the list of VM names..
error is as follows:
(type string). If this does not look like what you expect, quote the entire
value to ensure it does not change.
fatal: [127.0.0.1]: FAILED! => {"changed": false, "msg": "Failed to find the managed object for [
then spits out the list separated with \name\
what am I doing wrong here?
- hosts: localhost
vars_prompt:
- name: vcenter_hostname
prompt: Please provide the vcenter server hostname.
private: no
- name: vcenter_username
prompt: Please provide the vcenter server username.
private: no
- name: vcenter_password
prompt: Please provide the password for the username.
unsafe: yes
private: yes
vars:
vm_name: "{{lookup('file', '/etc/ansible/lists/lab_common_backup.txt')}}"
tasks:
- name: vm_tag_add
vmware_tag_manager:
hostname: '{{ vcenter_hostname }}'
username: '{{ vcenter_username }}'
password: '{{ vcenter_password }}'
validate_certs: no
tag_names:
- 'Labs: Common Backup'
object_name: "{{ vm_name }}"
object_type: VirtualMachine
state: remove
I was able to get this working by using .split(), the default delimiter for split is a space..
object_name: "{{ vm_name.split() }}"

Access element of list as dict

I'm trying to access the elements of a list, but whenever I try to access it's elements they are treated as text (see the debug output).
How can I loop over the elements of content[0]?
ansible version: 2.5.1
playbook.yml:
- hosts: localhost
gather_facts: no
vars:
content: []
tasks:
- name: Call the Consul API to get all registered services
uri:
url: "https://{{API_URL}}/v1/health/service/{{item}}?Status=critical"
headers:
Authorization: "Bearer {{TOKEN}}"
method: GET
return_content: yes
status_code: 200
body_format: json
force: yes
register: r
with_items:
- service1
- service2
- set_fact:
content: "{{ lookup('flattened', r | json_query('results[*].content')) }}"
- debug:
msg: "{{ content[0][0] | type_debug }}"
- debug:
msg: "{{ item | type_debug }}"
with_items: "{{ content[0] }}"
debug output:
TASK [debug]
ok: [127.0.0.1] => {
"msg": "dict"
}
TASK [debug]
ok: [127.0.0.1] => (item=None) => {
"msg": "AnsibleUnsafeText"
}
I'm hoping this question and answer will help you. I think the approach you are taking is the problem.