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.