I want to create pg_hba.conf entries with the community.postgresql.postgresql_pg_hba module. I think generally my issue here is not releated to how that module works though
I can do this with a TASK like the below ...
I want to create pg_hba.conf entries with the community.postgresql.postgresql_pg_hba module. I think generally my issue here is not releated to how that module works though
I can do this with a TASK like the below ...
The specific error, “‘list objec’ has no attribute ‘address’”, is pretty clear. You’re trying to take some short-cuts with your data manipulation, and Ansible can’t read your mind wrt what you’re really trying to accomplish. Your intuition is correct though, that this is not so much an issue with the module but rather a data manipulation problem.
I think I understand what you want, though. In fact, I wrote a filter years ago to avoid repeating data in similar circumstances. I’ve put off responding, partly because I had hoped to see what some of the regulars around ansible-project proposed, and partly because it’d be kind of crazy to suggest incorporating my filter into your project. However, no-one has responded yet, and the more I look at your problem the more it feels like this might be the shortest path to a workable solution.
Before I present my toy playbook, let me just say that you’ve thrown some real brain-benders at me with this data, embedding some fields within others and, if you really want DRY (don’t repeat yourself) even throwing in a dig with deferred Jinja2 evaluation. Ouch! Even still, I think the result, which can all go into a single vars section, is more maintainable that a bunch of set_fact tasks doing obscure Jinja2 tricks.
Enough qualifiers. Here’s my toy playbook:
---
- name: Abstracting loop items
hosts: localhost
tasks:
- name: Debug some data manips
ansible.builtin.debug:
msg: "{{ kcl }}"
vars:
__kcl:
pw_crypt: scram-sha-256
conntype: host
db: keycloak
polymac:
- - ip: 172.27.130.214 # "10.4.253.146"
role: keycloak
- ip: 172.27.130.215
role: eycloakk
pw_crypt: cleartext
- ip: 172.27.130.216
role: ycloakke
- ip: 172.27.130.217
role: cloakkey
- address: '%ip%/32'
fqdn: "{{ '{' ~ '{' }} lookup('community.general.dig', '%ip%/PTR') | trim('.') }}"
comment: "%role% - {{ '{' ~ '{' }} lookup('community.general.dig', '%ip%/PTR') | trim('.') }}"
database: "%db%%role%"
pw_crypt: "%pw_crypt%"
kcl: "{{ lookup('items', __kcl | polymac ) }}"
And here’s the output:
TASK [Debug some data manips] ***************************************
ok: [localhost] => {
"msg": [
{
"address": "172.27.130.214/32",
"comment": "keycloak - itvpn-214.vpn.my.org",
"conntype": "host",
"database": "keycloakkeycloak",
"db": "keycloak",
"fqdn": "itvpn-214.vpn.my.org",
"pw_crypt": "scram-sha-256"
},
{
"address": "172.27.130.215/32",
"comment": "eycloakk - itvpn-215.vpn.my.org",
"conntype": "host",
"database": "keycloakeycloakk",
"db": "keycloak",
"fqdn": "itvpn-215.vpn.my.org",
"pw_crypt": "cleartext"
},
{
"address": "172.27.130.216/32",
"comment": "ycloakke - itvpn-216.vpn.my.org",
"conntype": "host",
"database": "keycloakycloakke",
"db": "keycloak",
"fqdn": "itvpn-216.vpn.my.org",
"pw_crypt": "scram-sha-256"
},
{
"address": "172.27.130.217/32",
"comment": "cloakkey - itvpn-217.vpn.my.org",
"conntype": "host",
"database": "keycloakcloakkey",
"db": "keycloak",
"fqdn": "itvpn-217.vpn.my.org",
"pw_crypt": "scram-sha-256"
}
]
}
The polymac filter is available in a gist on github at https://gist.github.com/utoddl/fc303d5b9dcad58871d2956cfed0aa68.
Some explanation is in order. Lots, in fact. “polymac” is both a filter, and a key in the __kcl dict. The filter looks for such eponymous keys to find its instructions regarding how the dict should be transformed. Each polymac key (there can be more than one) contains a list. The first item defines one or more sets of “input” data. In this case, it’s four sets of data, and each of those contains an “ip” and a “role”. Note that one of them also contains a “pw_crypt” string of value “cleartext”. More on that later.
So, that’s polymac[0] described. Subsequent elements of the polymac list (in this case there is only one) contain templates for the output dicts. In this example, the sole template dict contains five field definitions: “address”, “fqdn”, “comment”, “database”, and “pw_crypt”. However, if you look at the output, each resulting dict contains seven attributes, not five. These additional attributes are the three siblings of the original “polymac” key: “pw_crypt”, “conntype”, and “db”, and they act as defaults for all the generated dicts. Note that one of these attributes, “pw_crypt” is also in the sole template dict where it has the value “%pw_crypt%”.
Notice that the output dicts contain all the keys that are siblings of “polymac” and all the keys in the template(s). The output dicts however do not contain attributes corresponding to the “input” keys, i.e. those contained at some level within polymac[0].
You’ve probably figured out by now that template values containing “input” keys or “sibling” keys sandwiched between percent signs get those parts of their strings substituted by the indicated “input” or “sibling” values. This is how, for example, “address: ‘%ip%/32’” becomes the “input” dict’s “ip” value with “/32” concatenated to it.
Remember that one “input” dict that contains “pw_crypt: ‘cleartext’”? For the corresponding output dict, ‘cleartext’ takes precedence over the “sibling” value of “scram-sha-256”. For all the others, the “sibling” value acts as a default.
That pretty much covers “polymac” for this example. The rest is an exploration of generating and forcing evaluation of Jinja2 expressions within strings. Consider what’s going on with
fqdn: "{{ '{' ~ '{' }} lookup('community.general.dig', '%ip%/PTR') | trim('.') }}"
The first part of that expression - the bit between the opening and closing mustaches - actually creates another opening mustache! In the mean time, the “%ip%” gets the relevant IPv4 address inserted. So on the very last line of the example playbook, “__kcl | polymac” produces a list of dicts, but where the fqdn should be, there is instead “{{ lookup(‘community.general.dig’, ‘172.27.130.214/PTR’) | trim(‘.’) }}” for example. In fact, it’s in each output dict twice. (I tried to work around that, but life is short enough! Left as an exercise for the reader.)
The final bit then is feeding the entire list of dicts with embedded Jinja2 to the “items” lookup. This has the necessary effect of evaluating all the embedded Jinja2 expressions while copying the list to create “kcl”. Note that it’s “kcl” rather than “__kcl” that the debug task prints.
That’s how I would handle this problem, but I wrote and can support the polymac filter for my own purposes. Whether anyone else should consider it is up to them, but at least you’re aware of the possibility.
Good luck, and let us know how you get on.