Access nested items on a dictionary ansible - json

I have a json file with server information and the applications related.
Something like this:
{
"Apps": [
{
"AppOwner": "Me",
"AppNm": "Vacations"
}
],
"Hostnm": "some_server",
"Environment": "Prod",
"OSnm": "Windows Server 2008",
"OSManu": "Microsoft",
"SerialNum": "VMware-42 14 e1 37 7a 63 9b 0e-43 07 15 46 64 9c 3c 12"
}
I want to create a list with all the applications and the servers related
For example
{
"Vacations": [server1, server2]
},
{
"other_app": [server2, server3]
},
For that, I have been trying the following.
- set_fact:
apps: "{{ apps }} + {{ item.Apps | map(attribute='AppNm') | list}}"
loop: "{{ server_changes }}"
- set_fact:
apps_revised: "{{apps | unique}}"
- set_fact:
apps_server: "{{ apps_revised | default([]) + [{item : []}] }}"
with_items: "{{ apps_revised }}"
The first part of the code, will obtain all the applications with the duplicates from the json.
The second part of the code, will clean the apps with unique.
The last part its going to create a list of application with a nested list (of servers).
The problem is that I don't understand how to navigate the nested server_changes application list and then add the server to my new list.
Probably there is an easier way to do it, so any help will be appreciated.
Thanks

Given a test data
server_changes:
- "Apps":
- {"AppOwner": "Me", "AppNm": "Vacations"}
"Hostnm": "server1"
- "Apps":
- {"AppOwner": "Me", "AppNm": "Vacations"}
- {"AppOwner": "Me", "AppNm": "other_app"}
"Hostnm": "server2"
- "Apps":
- {"AppOwner": "Me", "AppNm": "other_app"}
"Hostnm": "server3"
Let's simplify the data. For example
- set_fact:
my_server_changes: "{{ my_server_changes|default([]) +
[{'hostname': item.Hostnm,
'applications': item.Apps|
json_query('[].AppNm')}] }}"
loop: "{{ server_changes }}"
- debug:
var: my_server_changes
gives
"my_server_changes": [
{
"applications": [
"Vacations"
],
"hostname": "server1"
},
{
"applications": [
"Vacations",
"other_app"
],
"hostname": "server2"
},
{
"applications": [
"other_app"
],
"hostname": "server3"
}
]
Create the list of the applications
- set_fact:
my_apps: "{{ server_changes|json_query('[].Apps[].AppNm')|unique }}"
- debug:
var: my_apps
gives
"my_apps": [
"Vacations",
"other_app"
]
Then loop the list of the applications, select records that contains the item and combine the dictionary
- set_fact:
apps_server: "{{ apps_server|default({})|
combine({item: my_server_changes|json_query(query)}) }}"
loop: "{{ my_apps }}"
vars:
query: "[?applications.contains(#, '{{ item }}')].hostname"
- debug:
var: apps_server
gives
"apps_server": {
"Vacations": [
"server1",
"server2"
],
"other_app": [
"server2",
"server3"
]
}

Related

How many times item into a json

I have a json file (/tmp/lst_instalaciones_wordpress.json) on a group of servers.
This json has the info about some Wordpress installs (domain, path, version...)
{
"id": 1,
"name": "My blog",
"version": "5.9.1",
"fullPath": "/var/www/vhosts/myblog"
}
{
"id": 2,
"name": "My blog2",
"version": "5.9.2",
"fullPath": "/var/www/vhosts/myblog2"
}
{
"id": 3,
"name": "My blog3",
"version": "5.7.6",
"fullPath": "/var/www/vhosts/myblog3"
}
{
"id": 4,
"name": "My blog4",
"version": "5.9.2",
"fullPath": "/var/www/vhosts/myblog4"
}
I slurped it in a var:
- slurp:
src: /tmp/lst_instalaciones_wordpress.json
register: fichero_json
- set_fact:
lst_versiones: "{{ fichero_json.content | b64decode | from_json}}"
I have a list of all the versions, and I need to print how much installs are for each versions.
all_vers:
- 5.7.6
- 5.9.1
- 5.9.2
I'am trying different options like this but nothing works:
- set_fact:
all_installs_vers: "{{ lst_versiones | json_query('[?version==`'{{item}}'`].version') | length }}"
with_items: "{{all_vers}}"
Given the fixed YAML
shell> cat tmp/lst_instalaciones_wordpress.yml
---
[
{
"id": 1,
"name": "My blog",
"version": "5.9.1",
"fullPath": "/var/www/vhosts/myblog"
},
{
"id": 2,
"name": "My blog2",
"version": "5.9.2",
"fullPath": "/var/www/vhosts/myblog2"
},
{
"id": 3,
"name": "My blog3",
"version": "5.7.6",
"fullPath": "/var/www/vhosts/myblog3"
},
{
"id": 4,
"name": "My blog4",
"version": "5.9.2",
"fullPath": "/var/www/vhosts/myblog4"
}
]
Read the file and decode the list
- slurp:
src: tmp/lst_instalaciones_wordpress.yml
register: fichero_json
- set_fact:
lst_versiones: "{{ fichero_json.content|b64decode|from_yaml }}"
gives
lst_versiones:
- fullPath: /var/www/vhosts/myblog
id: 1
name: My blog
version: 5.9.1
- fullPath: /var/www/vhosts/myblog2
id: 2
name: My blog2
version: 5.9.2
- fullPath: /var/www/vhosts/myblog3
id: 3
name: My blog3
version: 5.7.6
- fullPath: /var/www/vhosts/myblog4
id: 4
name: My blog4
version: 5.9.2
Then, given the list of the versions
all_vers:
- 5.7.6
- 5.9.1
- 5.9.2
iterate the list, select the version, count the items and combine the dictionary
- set_fact:
all_installs_vers: "{{ all_installs_vers|d({})|
combine({item: _val|int}) }}"
loop: "{{ all_vers }}"
vars:
_val: "{{ lst_versiones|selectattr('version', 'eq', item)|length }}"
gives
all_installs_vers:
5.7.6: 1
5.9.1: 1
5.9.2: 2
Another solution with only one step
- set_fact:
versions: "{{ versions | d({}) | combine({ item.version: nbversion }) }}"
loop: '{{ lst_versiones }}'
vars:
nbversion: >-
{{ (versions[item.version] | int + 1) if versions[item.version] is defined
else (1 | int) }}
result if you debug versions:
ok: [localhost] => {
"msg": {
"5.7.6": "1",
"5.9.1": "1",
"5.9.2": "2"
}
}

Loop over multiple JSON arrays to filter the relevant JSON object based on a key value in Ansible

I am trying to loop over 2 different JSON array and filter only if the 2 key values are same.
I am comparing cidr from po-orig.json/po file and subnet from pocooling json. If the key values are same in both file, then print the pertaining details of pocooling json file as a list.
- name: Combine GP MGMT
vars:
pocidr: >-
{{
po
| json_query('ansible_facts.policyobject[].json[]')
}}
poname: >-
{{
pocidr
| selectattr('cidr', '==', item.subnet)
| map(attribute='name')
| list
}}
set_fact:
result: >-
{{
result | default([])
+
[poname]
}}
with_items: "{{ pocooling }}"
Updated code
- name: Compare the Current & Actual GP policy
set_fact:
pocidr: "{{ po | json_query(\"ansible_facts.policyobject[].json[]\") }}"
- name: Combine GP MGMT
vars:
poname:
"{{
pocidr
| selectattr('cidr', '==', item.subnet)
| map(attribute='name')
| first
}}"
set_fact:
result:
"{{
result | default([])
+
[poname]
}}"
with_items: "{{ pocooling }}"
pocooling.json
[
{
"applianceIp": "10.10.10.10",
"dhcpBootOptionsEnabled": false,
"dhcpHandling": "Run a DHCP server",
"dhcpLeaseTime": "1 day",
"dhcpOptions": [],
"dnsNameservers": "upstream_dns",
"fixedIpAssignments": {},
"id": 2222,
"name": "Cooling",
"networkId": "78984654989",
"reservedIpRanges": [],
"subnet": "10.10.10.0/28"
},
{
"applianceIp": "10.10.10.10",
"dhcpBootOptionsEnabled": false,
"dhcpHandling": "Run a DHCP server",
"dhcpLeaseTime": "1 day",
"dhcpOptions": [],
"dnsNameservers": "upstream_dns",
"fixedIpAssignments": {},
"id": 2222,
"name": "Cooling",
"networkId": "123456789",
"reservedIpRanges": [],
"subnet": "10.11.11.0/28"
}
]
po-orig.json(po)
{
"ansible_facts":{
"policyobject":[
{
"invocation":{
"module_args":{
"status_code":[
200,
201,
202
],
"validate_certs":false
}
},
"item":{
"id":"123456",
"name":"DC"
},
"json":[
{
"category":"network",
"cidr":"10.1.1.1/28",
"createdAt":"2021-11-23T19:48:21Z",
"groupIds":[
],
"id":"545649843651365",
"name":"JM-Privat",
"networkIds":[
"545649843651365"
],
"type":"cidr",
"updatedAt":"2021-11-23T19:48:21Z"
},
{
"category":"network",
"cidr":"10.10.10.0/28",
"createdAt":"2021-12-07T13:54:05Z",
"groupIds":[
"897987654689854"
],
"id":"564678984565465",
"name":"PO-L-DE-MCC-COOLING_DMZ_LAB",
"networkIds":[
],
"type":"cidr",
"updatedAt":"2021-12-07T13:54:05Z"
}
]
}
]
}
}
Error:
fatal: [localhost]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: {{ pocidr\n | selectattr('cidr', '==', item.subnet)\n | map(attribute='name')\n | list\n}}: 'dict object' has no attribute 'cidr
Expected result
[
{
"applianceIp": "10.10.10.10",
"dhcpBootOptionsEnabled": false,
"dhcpHandling": "Run a DHCP server",
"dhcpLeaseTime": "1 day",
"dhcpOptions": [],
"dnsNameservers": "upstream_dns",
"fixedIpAssignments": {},
"id": 2222,
"name": "Cooling",
"networkId": "78984654989",
"reservedIpRanges": [],
"subnet": "10.10.10.0/28"
}
]
Complete Playbook
- name: Test
hosts: localhost
connection: local
gather_facts: false
vars:
pocooling: "{{ lookup('file', 'pocooling.json') | from_json }}"
tasks:
- include_vars:
file: po-orig.json
name: po
- name: Combine GP MGMT
vars:
poname: "{{ po|json_query('ansible_facts.policyobject[].json[]')|
selectattr('cidr', '==', item.subnet)|
map(attribute='name')|
list }}"
set_fact:
result: "{{ result|default([]) + [poname] }}"
loop: "{{ pocooling }}"
Given the list
pocooling:
- networkId: '78984654989'
subnet: 10.10.10.0/28
- networkId: '123456789'
subnet: 10.11.11.0/28
Select list of cidr
_cidr: "{{ ansible_facts.policyobject|json_query('[].json[].cidr') }}"
gives
_cidr:
- 10.1.1.1/28
- 10.10.10.0/28
Then select the items that fit the condition
- debug:
msg: "{{ pocooling|selectattr('subnet', 'in' , _cidr) }}"
vars:
_cidr: "{{ ansible_facts.policyobject|json_query('[].json[].cidr') }}"
gives
msg:
- networkId: '78984654989'
subnet: 10.10.10.0/28
Your code, fixed and simplified
- include_vars:
file: po-orig.json
name: po
- name: Combine GP MGMT
vars:
poname: "{{ po|json_query('ansible_facts.policyobject[].json[]')|
selectattr('cidr', '==', item.subnet)|
map(attribute='name')|
list }}"
set_fact:
result: "{{ result|default([]) + [poname] }}"
loop: "{{ pocooling }}"
gives
result:
- - PO-L-DE-MCC-COOLING_DMZ_LAB
- []

ansible obtain values from changing key

I would like to get value from json, but one of the key can be differet.
here is example json
{
"json": {
"id": "9758b1e5-442e-4545-9364-45f28477edfb",
"results": [{
"code": 200,
"host": "localhost",
"message": "no change",
"runTime": 1233,
"tenant": "http-validate-2.usa-dc.com"
}],
"traces": {
"http-validate-2.usa-dc.comCurrent": {
"/Common/10.10.100.10": {
"command": "ltm node"
},
"http-validate-2.usa-dc.comDiff": [{
"command": "ltm virtual",
"kind": "D",
"lhs": {
"default": "yes"
},
"lhsCommand": "ltm virtual",
"path": [
"/http-validate-2.usa-dc.com/app/vs_http-validate-2.usa-dc.com_80",
"properties",
"persist",
"/Common/cookie"
],
"rhsCommand": "ltm virtual",
"tags": [
"tmsh"
]
}]
}
}
}
}
my ansible playbook
tasks:
- name : deploy json file AS3 to F5
debug:
msg: "{{ lookup('file', 'parse.json') }}"
register: atc_AS3_status
- name: debug
debug:
msg: "{{ atc_AS3_status.msg.json['traces']['.*Diff']}}"
I would like to reach key "path" but the key above "http-validate-2.usa-dc.comDiff" can be different like "http-validate-3.can-dc.comDiff" but always finish with Diff
Use json_query, e.g.
- debug:
msg: "{{ json.traces|json_query('*.*[][].path') }}"
should give the list of the paths (there might be more of them)
msg:
- - /http-validate-2.usa-dc.com/app/vs_http-validate-2.usa-dc.com_80
- properties
- persist
- /Common/cookie
Q: "I need path only in the key which ends Diff."
A: JMESPath is not able to search key wildcards, AFAIK. Instead, use select and create the list of the nested keys that match the regex, e.g.
- debug:
msg: "{{ json.traces|json_query('*.keys(#)')|flatten|
select('match', '^.*Diff$')|list }}"
gives
msg:
- http-validate-2.usa-dc.comDiff
Then iterate this list, select path and concatenate the list paths, e.g.
- set_fact:
paths: "{{ paths|d([]) + json.traces|json_query(query) }}"
loop: "{{ json.traces|json_query('*.keys(#)')|flatten|
select('match', '^.*Diff$')|list }}"
vars:
query: '*."{{ item }}"[].path'
gives the list of paths for the keys that match the regex
paths:
- - /http-validate-2.usa-dc.com/app/vs_http-validate-2.usa-dc.com_80
- properties
- persist
- /Common/cookie

Create list of key-values from JSON file in Ansible

My data is in json file as shown below:
vmdata.json
{
"VMDetails":
[
{
"name": "Owner1",
"vms": [ "vm10", "vm11", "vm12", "vm13" ]
},
{
"name": "Owner2",
"vms": [ "vm20", "vm21", "vm22", "vm23" ]
},
{
"name": "Owner3",
"vms": [ "vm30", "vm31", "vm32", "vm33" ]
}
]
}
I need this json data converted to list of key-value pairs and later used for tagging the VMs on vcenter.
Owner1: vm11
Owner1: vm12
Owner1: vm13
Owner1: vm14
Owner2: vm21
Owner2: vm22
Owner2: vm23
Owner2: vm24
Owner3: vm31
Owner3: vm32
Owner3: vm33
Owner3: vm34
I have assigned the content from the data file to variable using :
vms_tobe_tagged: "{{ lookup ('file', 'vmtags.json')| from_json}}"
I query and get the list of owners using this and it works well:
- set_fact:
Owner: "{{ vms_tobe_tagged| json_query('OwnerDetails[*].name') }}"
- name: Test loop
debug:
msg: "{{ Owner }}"
Is it possible to generate the data using this?
loop: "{{ ['alice', 'bob'] |product(['clientdb', 'employeedb', 'providerdb'])|list }}"
Q: "I need this json data converted to a list of key-value pairs ..."
Owner1: vm11
Owner1: vm12
Owner1: vm13
Owner1: vm14
Owner2: vm21
...
Is it possible to generate the data using 'product' filter?
A: Yes. With the product filter it's possible to create a list that comprises all products. The list of the key-value pairs can be created in the next loop. For example the tasks below
- set_fact:
owner_list: "{{ owner_list|
default([]) +
[item.name]|product(item.vms)|list }}"
loop: "{{ vms_tobe_tagged.VMDetails }}"
- set_fact:
owner: "{{ owner|
default([]) +
[{item.0: item.1}] }}"
loop: "{{ owner_list }}"
- debug:
var: owner
give
"owner": [
{
"Owner1": "vm10"
},
{
"Owner1": "vm11"
},
{
"Owner1": "vm12"
},
{
"Owner1": "vm13"
},
{
"Owner2": "vm20"
...
This helped me. I used json_query, with_subelements to iterate.
- set_fact:
VMOwnerdetails: "{{ vms_tobe_tagged| json_query('VMDetails[*]') }}"
- name: Verbose updates
with_subelements:
- "{{VMOwnerdetails}}"
- vms
debug:
msg: "{{ item.0.name }} : {{ item.1 }}"

best way to modify json in ansible

I have a variable (via set_fact) containing a json string:
{
"PolicyVersion": {
"CreateDate": "2017-08-07T02:48:05Z",
"Document": {
"Statement": [
{
"Action": "sts:AssumeRole",
"Effect": "Allow",
"Resource": [
"arn:aws:iam::123456789123:role/Root_Update_svcacct",
"arn:aws:iam::123456789123:role/Root_Delete_svcacct",
"arn:aws:iam::123456789123:role/Root_Create_svcacct",
"arn:aws:iam::123456789123:role/Root_Full_svcacct",
"arn:aws:iam::987654321987:role/Member1_Create_svcacct",
"arn:aws:iam::987654321987:role/Member1_Update_svcacct",
"arn:aws:iam::987654321987:role/Member1_Delete_svcacct",
"arn:aws:iam::987654321987:role/Member1_Full_svcacct"
]
}
],
"Version": "2012-10-17"
},
"IsDefaultVersion": true,
"VersionId": "v2"
}
}
What is the best way to insert more elements in the "Resource" array?
"arn:aws:iam::001122334455:role/Member1_Create_svcacct",
"arn:aws:iam::001122334455:role/Member1_Update_svcacct",
"arn:aws:iam::001122334455:role/Member1_Delete_svcacct",
"arn:aws:iam::001122334455:role/Member1_Full_svcacct"
I am exploring dumping the variable to a file and inserting the block I want with external shell tools, which does not seem to be elegant.
I don't know about the best way, but one option is to write a simple library module to handle the mechanics of the update for you. You could use the jsonpointer module as a way of locating the data you wish to modify, and then return the modified object to ansible. A starting point might look like:
#!/usr/bin/python
from ansible.module_utils.basic import AnsibleModule
import json
try:
import jsonpointer
except ImportError:
jsonpointer = None
def main():
module = AnsibleModule(
argument_spec=dict(
data=dict(required=True, type='dict'),
pointer=dict(required=True),
action=dict(required=True,
choices=['append', 'extend', 'update']),
update=dict(type='dict'),
extend=dict(type='list'),
append=dict(),
),
supports_check_mode=True,
)
if jsonpointer is None:
module.fail_json(msg='jsonpointer module is not available')
action = module.params['action']
data = module.params['data']
pointer = module.params['pointer']
if isinstance(data, str):
data = json.loads(str)
try:
res = jsonpointer.resolve_pointer(data, pointer)
except jsonpointer.JsonPointerException as err:
module.fail_json(msg=str(err))
if action == 'append':
res.append(module.params['append'])
if action == 'extend':
res.extend(module.params['extend'])
elif action == 'update':
res.update(module.params['update'])
module.exit_json(changed=True,
result=data)
if __name__ == '__main__':
main()
If you drop this into, e.g., library/json_modify.py, you can use it in a playbook like this:
- hosts: localhost
gather_facts: false
vars:
myvar: {
"PolicyVersion": {
"CreateDate": "2017-08-07T02:48:05Z",
"Document": {
"Statement": [
{
"Action": "sts:AssumeRole",
"Effect": "Allow",
"Resource": [
"arn:aws:iam::123456789123:role/Root_Update_svcacct",
"arn:aws:iam::123456789123:role/Root_Delete_svcacct",
"arn:aws:iam::123456789123:role/Root_Create_svcacct",
"arn:aws:iam::123456789123:role/Root_Full_svcacct",
"arn:aws:iam::987654321987:role/Member1_Create_svcacct",
"arn:aws:iam::987654321987:role/Member1_Update_svcacct",
"arn:aws:iam::987654321987:role/Member1_Delete_svcacct",
"arn:aws:iam::987654321987:role/Member1_Full_svcacct"
]
}
],
"Version": "2012-10-17"
},
"IsDefaultVersion": true,
"VersionId": "v2"
}
}
tasks:
- json_modify:
data: "{{ myvar }}"
pointer: "/PolicyVersion/Document/Statement/0/Resource"
action: extend
extend:
- "arn:aws:iam::001122334455:role/Member1_Create_svcacct"
- "arn:aws:iam::001122334455:role/Member1_Update_svcacct"
- "arn:aws:iam::001122334455:role/Member1_Delete_svcacct"
- "arn:aws:iam::001122334455:role/Member1_Full_svcacct"
register: result
- debug:
var: result.result
The result of running this playbook and the proposed module is:
TASK [debug] *******************************************************************
ok: [localhost] => {
"result.result": {
"PolicyVersion": {
"CreateDate": "2017-08-07T02:48:05Z",
"Document": {
"Statement": [
{
"Action": "sts:AssumeRole",
"Effect": "Allow",
"Resource": [
"arn:aws:iam::123456789123:role/Root_Update_svcacct",
"arn:aws:iam::123456789123:role/Root_Delete_svcacct",
"arn:aws:iam::123456789123:role/Root_Create_svcacct",
"arn:aws:iam::123456789123:role/Root_Full_svcacct",
"arn:aws:iam::987654321987:role/Member1_Create_svcacct",
"arn:aws:iam::987654321987:role/Member1_Update_svcacct",
"arn:aws:iam::987654321987:role/Member1_Delete_svcacct",
"arn:aws:iam::987654321987:role/Member1_Full_svcacct",
"arn:aws:iam::001122334455:role/Member1_Create_svcacct",
"arn:aws:iam::001122334455:role/Member1_Update_svcacct",
"arn:aws:iam::001122334455:role/Member1_Delete_svcacct",
"arn:aws:iam::001122334455:role/Member1_Full_svcacct"
]
}
],
"Version": "2012-10-17"
},
"IsDefaultVersion": true,
"VersionId": "v2"
}
}
}
Actually Ansible is natively able to read JSON files.
see this question:
add-a-new-key-value-to-a-json-file-using-ansible
This is a little bit old. I know but I felt it could be useful to many people so I'll post here my solution as "the best way to modify a json" that I could personally find.
First of all the JSON. In my use-case I had a bunch of aws snapshots that were encrypted with the wrong kms key and I had to recreate the AMI with the correct key.
So I had to :
get data of the old snapshots ( like size device_name etc.)
create the new snaps with the different key
re-register a new ami with the correct block_device_mapping
Here's the code
- name: get ami
amazon.aws.ec2_ami_info:
image_ids: ami-<id>
region: "{{ region }}"
register: ami
- name: save snapshot ids and device_name and volume_size
set_fact:
snapshot_ids: "{{ ami | json_query('images[].block_device_mappings[].ebs.snapshot_id') }}"
device_name: "{{ ami | json_query('images[].block_device_mappings[].device_name') }}"
volume_size: "{{ ami | json_query('images[].block_device_mappings[].ebs.volume_size') }}"
basically each of the above is a list of each of the 3 things (device_name, snap_id, volume_size) that I need (but it could be extended)
then:
- name: get kms arn
aws_kms_info:
filters:
alias: "{{ kms_keys.alias }}"
region: "{{ region }}"
register: aws_kms_facts_out
- debug:
var: aws_kms_facts_out
- set_fact:
kms_arn: "{{ aws_kms_facts_out['keys'][0].key_arn }}"
- name: copy snap with new encryption key
community.aws.ec2_snapshot_copy:
source_region: "{{ region }}"
region: "{{ region }}"
source_snapshot_id: "{{ item }}"
encrypted: yes
kms_key_id: "{{ kms_arn }}"
loop: "{{ snapshot_ids }}"
register: new_snapshots
and then here's the catch
- set_fact:
new_snapshot_ids: "{{ new_snapshots| json_query('snapshots[].snapshot_id') }}"
- name: creating the block_device_mappings structure (still a list of objects)
ansible.builtin.debug:
msg: '{
"device_name": "{{ item.2 }}",
"delete_on_termination": "true",
"snapshot_id": "{{ item.0 }}",
"volume_size": {{ item.1 }},
"volume_type": "gp2"
}'
loop: "{{ new_snapshot_ids|zip_longest(volume_size,device_name)|list }}"
register: block_device_mappings
- set_fact:
bdm: "{{ block_device_mappings | json_query('results[].msg') }}"
finally
- name: create new ami from newly created snapshots
amazon.aws.ec2_ami:
region: "{{ region }}"
name: "{{ instance.name }}-{{ ansible_date_time.date }}"
state: present
architecture: x86_64
virtualization_type: hvm
root_device_name: /dev/sda1
device_mapping: "{{ bdm }}"
This is how you can do it without requiring any additional trick.
Of course this is declined to my particular use case but you can adapt it to any circumstance, that do not require a complete disassemble and reassemble of the Json itself