Nested loop with a list and a dictionary

2019-02-15 16:29发布

问题:

So I'm sort of stuck on an issue. Essentially, I would like to run a nested loop with both a list and a dictionary, and I can't seem to figure out how.

My use case is running a playbook that will generate SSH certificates by signing public keys with a list of principals.

I have a list of hosts (group called bastionHosts) that act as jump-hosts. These hosts have end-users and their SSH keypair on them.

[bastionHosts]
10.100.0.10
10.100.0.11

I also have a dictionary of users and their SSH principals. The dictionary looks like this:

users:
   webuser01: "webservers-principal"
   dbuser01: "dbservers-principal"
   webadmin01: "webservers-principal"

I use the fetch module to download the public keys ("id_ecdsa.pub" for each user. This creates a directory structure that looks like: "/tmp/public_keys/<>/home/<< username>>/.ssh/id_ecdsa.pub

For example, fetching the public key for webuser01 on bastion 10.100.0.10 would place the key into /tmp/public_keys/10.100.0.10/home/webuser01/.ssh/id_ecdsa.pub on the ansible host.

My goal is to recurse down into this directory structure and sign each public key with the appropriate principals, but I can't figure out how to do that.

The code below would work if I was only fetching one set of files and it was a flat fetch (i.e. the directory structure for each host wasn't used). item.key is the username, and item.value is the set of principals.

 - name: create signed certificates with appropriate principals
   command: ssh-keygen -s /etc/ssh/CA/CA -I {{ item.key }} -n {{ item.value }} -V +1d /tmp/public_keys/{{ item.key }}.pub
   with_dict:
   "{{ users }}"

I need a way to also specify the host from the build-in groups.bastionHosts so that I can traverse the directory structure.The below code is a rough idea of what I'm looking to do, but I can't find a way to get with_nested to work with a list and a dictionary.

- name: create signed certificates with appropriate principals
  command: ssh-keygen -s /etc/ssh/CA/CA -I {{ item.key }} -n {{ item[1].value }} -V +1d /tmp/public_keys/{{ item[0] }}/home/{{ item[1].key }}/.ssh/id_ecdsa.pub
  with_nested:
  - "{{ groups.bastionHosts }}
   - "{{ users }}"

Essentially, if the above worked, it would walk down into each directory for each bastion host (item0) and user (item[1].key) and sign the key with the appropriate principals (item[1].value). However, this doesn't work. I get errors that item[1] doesn't have an element "value." I imagine this is because with_nested is supposed to work with lists, and not dictionaries.

So basically: is there a way to get with_nested to work with dictionaries and lists such that I can reference elements of a list while also referencing items within a dictionary? If not, am I perhaps making this too complicated and there's an easier way? Thanks in advance!

回答1:

There is a hack out there: you can make a list from dict with dictsort filter:

- hosts: localhost
  gather_facts: no
  vars:
    bastionHosts:
      - 10.100.0.10
      - 10.100.0.11
    users:
       webuser01: "webservers-principal"
       dbuser01: "dbservers-principal"
       webadmin01: "webservers-principal"
  tasks:
    - debug: msg="Host={{item.0}} User.Key={{item.1}} User.Value={{item.2}}"
      with_nested:
        - "{{ bastionHosts }}"
        - "{{ users | dictsort }}"


标签: ansible