templating file with ansible to get different variable parts (through its index) for different hosts
I'm trying to distribute certificates to its corresponding hosts (I'll just give the example for the private key task):
- name: create certificate private key
community.crypto.openssl_privatekey:
path: "/root/client/{{ item }}.key"
type: Ed25519
backup: yes
return_content: yes
register: privatekey
loop: "{{ ansible_play_hosts_all }}"
when: "'prometheus1' in inventory_hostname"
I can invoke the variable for other hosts like this:
{{ hostvars['prometheus1']['privatekey']['results'][0]['privatekey'] }}
The index points to a certain key, so 0 is going to be the first host (prometheus1
), 1 the second one and so on.
I suppose templating would be the way to go, but I simply don't know how to compose the template.
I think ansible_play_hosts_all
is key to the solution, in that its index corresponds to the private key's index, so for instance:
ansible_play_hosts_all[2]
--> hostvars['prometheus1']['privatekey']['results'][2]['privatekey']
But the logic would be:
for i in index of ansible_play_hosts_all
add the hostvars['prometheus1']['privatekey']['results'][i]['privatekey']
if ansible_play_hosts_all[i] in inventory_hostname
Something to that effect, I suppose :) Any help is greatly appreciated.
Update
Maybe something slightly more accurate:
{% for i in ansible_play_hosts_all|length) %}
{{ hostvars['prometheus1']['privatekey']['results'][i]['privatekey'] }}
{% endfor %}
and to it add the condition:
{% if ansible_play_hosts_all[i] in inventory_hostname %}
Solution 1:
It would be simpler to use delegation to create the keys on prometheus1
while leaving the registered variable associated with the correct host. Then you could just use privatekey.privatekey
in your template.
- name: create certificate private key
community.crypto.openssl_privatekey:
path: /root/client/{{ inventory_hostname }}.key
type: Ed25519
backup: yes
return_content: yes
register: privatekey
delegate_to: prometheus1
If you really want to stick to your current structure, you can find the list element corresponding to the current host by checking the item
value in the result, which contains the item
from that iteration of the loop.
{{ (hostvars['prometheus1']['privatekey']['results'] | selectattr('item', '==', inventory_hostname) | list | first).privatekey }}
Solution 2:
Create a dictionary with the data, e.g. given the inventory
shell> cat hosts
prometheus1 ansible_host=localhost
test_11 ansible_host=10.1.0.61 ansible_user=admin
test_12 ansible_host=10.1.0.62 ansible_user=admin
test_13 ansible_host=10.1.0.63 ansible_user=admin
the playbook
- hosts: all
gather_facts: false
tasks:
- community.crypto.openssl_privatekey:
path: "{{ playbook_dir }}/client/{{ item }}.key"
type: Ed25519
backup: yes
return_content: yes
register: privatekey
loop: "{{ ansible_play_hosts_all|difference(['prometheus1']) }}"
delegate_to: prometheus1
run_once: true
- set_fact:
host_priv: "{{ privatekey.results|
json_query('[].{host: item,
priv: privatekey}')|
items2dict(key_name='host', value_name='priv') }}"
run_once: true
generates the private keys for each host, except prometheus1, stores the keys in the files, and, from the registered data, creates a dictionary of the hosts and their keys
shell> tree client/
client/
├── test_11.key
├── test_12.key
└── test_13.key
ok: [prometheus1] =>
host_priv:
test_11: |-
-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VwBCIEIM7/BtpiM1EZxrrwtuE2VdSdr++3J/yxm/BnabnMqL3e
-----END PRIVATE KEY-----
test_12: |-
-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VwBCIEIPr8VV2RDOggNxo6vpBiXjSTzclJHFHaTVSxlFFVKoU1
-----END PRIVATE KEY-----
test_13: |-
-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VwBCIEIJjp2knmccffeEGvTNaP2f+ijXkeLmu89cGgkqFi771/
-----END PRIVATE KEY-----
Then, you can proceed in the play and copy the keys to the hosts, e.g.
- name: copy private key
copy:
content: "{{ host_priv[inventory_hostname] }}"
dest: /tmp/private.key
when: inventory_hostname != 'prometheus1'
become: true
Fit the paths to your needs and set the ownership and permissions as well.