How to print the results of multiple loops in a single debug message in Ansible?

I have a playbook that loops through a shell command for the values in a csv, modifies the results of the shell command with another loop and then prints them with a debug message.

I cannot seem to figure out how to output values from both loops into a single debug task. The tasks doing the work of the playbook are:

#get url certificate
- name: get url certificate
  become: false
  shell: echo | openssl s_client -showcerts -servername {{ loop_var_openssl_get_certificate_csv.url_base }} -connect {{ loop_var_openssl_get_certificate_csv.url_base }}:{{ loop_var_openssl_get_certificate_csv.url_port }} 2>/dev/null | openssl x509 -inform pem -noout -text
  loop: "{{ register_read_csv.list }}"
  loop_control:
    loop_var: loop_var_openssl_get_certificate_csv
  register: register_certificate

#extract cn from certificate
- name: extract cn from certificate
  set_fact:
    certificate_cn: "{{ item | regex_replace('\\s', '') | regex_search(regex_certificate_cn, '\\2') }}"
  loop: "{{ register_certificate.results }}"
  register: register_certificate_cn
  no_log: true

where the contents of my SCV are:

url_base,url_port
www.google.com,443
www.youtube.com,443

The first attempt at my debug message is looping through loop number 2, so I understand that I'm getting the entirety of loop 1's output, with the debug task looking like this sitting just below the extract cn from certificate task:

#print detailed list
- name: print detailed list
  debug:
    msg: "{{ register_read_csv.list }},{{ item.ansible_facts}}"
  loop: "{{ register_certificate_cn.results }}"
  loop_control:
    label: "{{ item.ansible_facts}}"

which outputs the following:

ok: [localhost] => (item={'certificate_cn': ['www.google.com']}) =>
  msg:
  - - url_base: www.google.com
      url_port: '443'
    - url_base: www.youtube.com
      url_port: '443'
  - certificate_cn:
    - www.google.com
ok: [localhost] => (item={'certificate_cn': ['*.google.com']}) =>
  msg:
  - - url_base: www.google.com
      url_port: '443'
    - url_base: www.youtube.com
      url_port: '443'
  - certificate_cn:
    - '*.google.com'

In the end I would like the msg to read something like:

ok: [localhost] =>
  msg:
  - - url_base: www.google.com
      url_port: 443
      certificate_cn: www.google.com
ok: [localhost] =>
  msg:
    - url_base: www.youtube.com
      url_port: 443
      certificate_cn: '*.google.com'

but I'm not quite sure how to get both loops' content to output how I'd like it to.

Upvotes: 1

Views: 2975

Answers (1)

β.εηοιτ.βε
β.εηοιτ.βε

Reputation: 39139

You can always access the item that was used at the creation of a results set via the item property — or the name you gave in the loop_var parameter — of the items in results.

So, in your actual trial, you could do something like this (without the need of the set_fact):

- debug:
    msg: >-
      {{ 
        item.loop_var_openssl_get_certificate_csv 
        | combine({
          'certificate_cn': item.stdout_lines 
          | select('contains', 'Subject: CN =')
          | first
          | replace('Subject: CN =', '')
          | trim
        }) 
      }}
  loop: "{{ register_certificate.results }}"
  loop_control:
    label: >-
      {{ item.loop_var_openssl_get_certificate_csv.url_base }}
      {{ item.loop_var_openssl_get_certificate_csv.url_port }}

This said, I would recommend you to reconsider your approach, as, feeding shell commands to Ansible, when there are dedicated modules doing what you need is not the good solution.

Here, the module community.crypto.get_certificate is exactly what you need, and it formats the certificat information in a dictionary already, so you don't have to process the result of it.

Given the playbook:

- hosts: localhost
  gather_facts: no

  tasks:
    - community.crypto.get_certificate:
        host: "{{ item.url_base }}"
        port: "{{ item.url_port }}"
      loop: "{{ urls }}"
      loop_control:
        label: "{{ item.url_base }}:{{ item.url_port }}"
      register: register_certificate
      vars:
        urls:
          - url_base: www.google.com
            url_port: 443
          - url_base: www.youtube.com
            url_port: 443

    - debug:
        msg: "{{ item.item | combine({'certificate_cn': item.subject.CN}) }}"
      loop: "{{ register_certificate.results }}"
      loop_control:
        label: "{{ item.item.url_base }}:{{ item.item.url_port }}"

This yields the expected:

TASK [community.crypto.get_certificate] *************************************
ok: [localhost] => (item=www.google.com:443)
ok: [localhost] => (item=www.youtube.com:443)

TASK [debug] ****************************************************************
ok: [localhost] => (item=www.google.com:443) => 
  msg:
    certificate_cn: www.google.com
    url_base: www.google.com
    url_port: 443
ok: [localhost] => (item=www.youtube.com:443) => 
  msg:
    certificate_cn: '*.google.com'
    url_base: www.youtube.com
    url_port: 443

Upvotes: 1

Related Questions