I have a list of web applications and their configs. Simplified data structure would look like this (list of dicts)
web_app_details:
- web_sourcedir: UWT_Optimus_UI
web_destdir: 'E:\alexsapp'
loadUserProfile: false
- web_sourcedir: UWT_Optimus_UI
web_destdir: 'E:\bodhi'
Now, there are some default values I don't want the user to specify every single time (single dict)
defaults_only:
identityType: LocalSystem
enable32BitAppOnWin64: false
loadUserProfile: true
If the user does not provide a value in any field within the defaults_only
dictionary, the defaults should apply. Otherwise, the defaults should be discarded.
So the result would be:
web_app_details:
- web_sourcedir: UWT_Optimus_UI
web_destdir: 'E:\alexsapp'
identityType: LocalSystem
enable32BitAppOnWin64: false
loadUserProfile: false # Because user provided the override value
- web_sourcedir: UWT_Optimus_UI
web_destdir: 'E:\bodhi'
identityType: LocalSystem
enable32BitAppOnWin64: false
loadUserProfile: true
I tried a few things such as
- Call a Powershell script to do this outside Ansible
- Duplicate the default dictionary as many times as in
web_app_detail
and then use combine filter - Insert
null
values with a JMESPath query:
- name: Pad web_app_details with null values
set_fact:
web_app_details_with_defaults: "{{ web_app_details | json_query(jmesquery) }}"
vars:
jmesquery:
"[*].{web_sourcedir: web_sourcedir, web_destdir: web_destdir, web_cleancopy: web_cleancopy }"
But nothing works.
Can someone please help?
CodePudding user response:
You could recreate that list adding the default with a combine
filter.
This will take advantage of the fact that a property defined in both dictionaries would be overridden by the dictionary you are combining with.
So, you'll end up with this set_fact
task:
- set_fact:
web_app_details_with_defaults: >-
{{
web_app_details_with_defaults | default([]) [
defaults_only | combine(item)
]
}}
loop: "{{ web_app_details }}"
Given the couple of tasks:
- set_fact:
web_app_details_with_defaults: >-
{{
web_app_details_with_defaults | default([]) [
defaults_only | combine(item)
]
}}
loop: "{{ web_app_details }}"
vars:
defaults_only:
identityType: LocalSystem
enable32BitAppOnWin64: false
loadUserProfile: true
web_app_details:
- web_sourcedir: UWT_Optimus_UI
web_destdir: 'E:\alexsapp'
loadUserProfile: false
- web_sourcedir: UWT_Optimus_UI
web_destdir: 'E:\bodhi'
- debug:
var: web_app_details_with_defaults
This yields the expected:
web_app_details_with_defaults:
- enable32BitAppOnWin64: false
identityType: LocalSystem
loadUserProfile: false
web_destdir: E:\alexsapp
web_sourcedir: UWT_Optimus_UI
- enable32BitAppOnWin64: false
identityType: LocalSystem
loadUserProfile: true
web_destdir: E:\bodhi
web_sourcedir: UWT_Optimus_UI
CodePudding user response:
There are multiple ways to approach this, but my preferred solution would be:
- hosts: localhost
vars:
web_app_details:
- web_sourcedir: UWT_Optimus_UI
web_destdir: 'E:\alexsapp'
loadUserProfile: false
- web_sourcedir: UWT_Optimus_UI
web_destdir: 'E:\bodhi'
defaults_only:
identityType: LocalSystem
enable32BitAppOnWin64: false
loadUserProfile: true
web_app_details_with_defaults: "{{ [defaults_only] | product(web_app_details) | map('combine') }}"
tasks:
- debug:
msg: "{{ web_app_details_with_defaults }}"
The expression {{ [defaults_only] | product(web_app_details) | map('combine') }}
creates a product of the lists [defaults_only]
(an inline single-element list) and web_app_details
, then uses map
to combine each list of dictionaries into a single dictionary.
PLAY [localhost] ***************************************************************
TASK [debug] *******************************************************************
ok: [localhost] => {
"msg": [
{
"enable32BitAppOnWin64": false,
"identityType": "LocalSystem",
"loadUserProfile": false,
"web_destdir": "E:\\alexsapp",
"web_sourcedir": "UWT_Optimus_UI"
},
{
"enable32BitAppOnWin64": false,
"identityType": "LocalSystem",
"loadUserProfile": true,
"web_destdir": "E:\\bodhi",
"web_sourcedir": "UWT_Optimus_UI"
}
]
}
If you are on an ancient version of Ansible without automatic unrolling you will need to add | list
at some points in the expression to convert generators into lists. Probably just {{ [defaults_only] | product(web_app_details) | map('combine') | list }}
, but I haven't tested this.