How to access the results of "register: results"

I have a task to collect information from a bunch of servers. Here is what I have started with:

  tasks:
    - name: Check cert end date
      shell: |
        date
        hostname
        openssl x509 -enddate -noout -in /etc/pki/vdsm/libvirt-spice/ca-cert.pem | cut -d "=" -f 2
        date -r /etc/pki/vdsm/libvirt-spice/ca-cert.pem -u
      args:
        executable: /bin/bash
        warn: false
      register: result

    - name: Result
      debug:
        msg: "{{ result }}"

How do I itinerate over result to produce a CSV file, one line per server?

You should be able to have a task that delegates to localhost (if you want the CSV file local), and use a loop through the data in the variable + ansible.builtin.lineinfile to add lines

1 Like

Don’t use lifeline, use a template with run_once and loop over all hosts, lineinfile does not handle parallelism and will end up overwriting entries.

template

host, date, hostname, certificate
{% for host in ansible_play_hosts %}
{% if 'result' in hostvars[host] %}
{{host}}, {{ hostvars[host]['result']['stdout'].split('\n', 3) | join(',') }}
{% endif %}
{% endfor %}

use in play

    - name: Create csv.
      template:
        path: csvfile.csv
        src:  mytemplate.j2
      delegate_to: localhost
      run_once: True
2 Likes

Yup, lineinfile does not handle parallelism well.

1 Like

I tried this:

    - name: Create csv
      template:
        dest: "{{ output_fn }}"
        src: mytemplate.j2
      delegate_to: localhost
      run_once: True

But I only get four lines, not the 17 I was expecting.

In looking at the output, I see four lines, all from the first server, one line for each piece of data I am looking for.

OK, I am missing something really basic here.

If I print ā€œresultā€, I get multiple elements(?), one for each server.

To make it simple, I tried this:

    - name: Result
      debug:
        msg: "{{ reand I get this:
.stdout
And  }}"ok: [compute13.slc.csswx.nas.faa.gov] => {
    "msg": "Thu Sep 26 17:37:01 UTC 2024\ncompute13.slc.csswx.nas.faa.gov\nDec  2 21:24:48 2033 GMT\nTue Dec  5 22:29:42 UTC 2023"
}
ok: [compute14.slc.csswx.nas.faa.gov] => {
    "msg": "Thu Sep 26 17:37:00 UTC 2024\ncompute14.slc.csswx.nas.faa.gov\nDec  2 21:24:48 2033 GMT\nTue Dec  5 22:34:19 UTC 2023"
}
ok: [compute15.slc.csswx.nas.faa.gov] => {
    "msg": "Thu Sep 26 17:37:00 UTC 2024\ncompute15.slc.csswx.nas.faa.gov\nDec  2 21:24:48 2033 GMT\nTue Dec  5 22:38:

18 UTC 2023"
}

How do I access each of these elements (or whatever they are called) in a loop?

The run_once is only for the template action, not the shell one.

@jrglynn2 don’t use connection: local this only works if controller and targets are configured the same, but for those in heterogeneous environments that breaks, use delegate_to: localhost

Here is where I am now:

- name: Add data to CSV file
  ansible.builtin.lineinfile:
    path: "{{ output_fn }}"
    line: "{{ result.stdout_lines[0]|string, result.stdout_lines[1]|string,result.stdout_lines[2]|string,result.stdout_lines[3]|string }}"
    state: present
  with_items: slc_computes

But the output in the file is this:

(u'Thu Sep 26 18:12:43 UTC 2024', u'compute01.slc.csswx.nas.faa.gov', u'Dec 2 21:24:48 2033 GMT', u'Tue Dec 5 21:28:15 UTC 2023')
Where are the ā€œuā€™ā€ coming from? How can I get rid of them and the parens?

the u is for ā€˜python unicode string’, it happens when you stringify some python objects using their default representation. The parens and the u show that you are stringifying a tuple, which you are creating with the , s

i’ve had problems with 2 forks, either your controller is slow parallelizing or your hosts have very different/stepped response times.

1 Like

What finally worked for me. Thanks for all your help.

    - name: Add data to CSV file
      connection: local
      ansible.builtin.lineinfile:
        path: "{{ output_fn }}"
        line: "{{ result.stdout_lines | list  | join(',') }}"
        state: present
        insertafter: EOF
      with_items: servers

Then you’ve gotten lucky (or just haven’t noticed when it failed to work.) This approach is not safe and should not be used ever. Even if it seems to work perfectly for you it is inherently a flawed pattern that relies on luck to win the race.

2 Likes

I“ve really only used lineinfile in conjunction with delegate_to: localhost and run_once: true as a gateway of last resort once - which should be ok if a specific module does not exist and template is unpractical. And yes, I would have noticed if it failed to work :slightly_smiling_face: However, I can see that parallel writing to the same file is not a good idea.

I totally understand why lineinfile is not a great option. I’ve never used templating before. I tried to use the code example you gave me but failed horribly. What other options do I have? I need to run this script against 250+ VMs to collect the data I need.

Thanks.

Dave

We are not talking about lineinfile in the abstract, we are talking about the concrete approach of using it to update a single file on localhost for multiple hosts, since that is what you suggested doing. Specifically, this was your suggestion:

    - name: Add data to CSV file
      delegate_to: localhost
      ansible.builtin.lineinfile:
        path: "/tmp/CSV-FILE.csv"
        line: "{{ tags.Name, private_ip_address, item.path }}"
        state: present
      with_items: "{{ file_list.files }}"

This introduces a race condition if it’s in a play with multiple targets and will eventually fail to record some data.

Figure out why you failed horribly? It works just fine.

- hosts: Class_mx
  tasks:
    - name: Check cert end date
      shell: |
        date
        hostname
        openssl x509 -enddate -noout -in /etc/pki/collab/private/mx.x.mail.umich.edu.ecc.crt | cut -d "=" -f 2
        date -r /etc/pki/collab/private/mx.x.mail.umich.edu.ecc.crt -u
      register: result

    - name: Create csv
      template:
        dest: csvfile.csv
        src:  mytemplate.j2
      delegate_to: localhost
      run_once: true
$ ansible-playbook ~/test.yml                          
                                                                                
PLAY [Class_mx] ****************************************************************
                                                                                
TASK [Check cert end date] *****************************************************
changed: [hushed-kameosa.mx.x.mail.umich.edu]                                   
changed: [laureate-ungaikyo.mx.x.mail.umich.edu]                              
changed: [small-jatai.mx.x.mail.umich.edu]                                      
                                        
TASK [Create csv.] *************************************************************
changed: [laureate-ungaikyo.mx.x.mail.umich.edu -> localhost]                   
                                                                                                                              
PLAY RECAP *********************************************************************                                              
hushed-kameosa.mx.x.mail.umich.edu : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
laureate-ungaikyo.mx.x.mail.umich.edu : ok=2    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0    
small-jatai.mx.x.mail.umich.edu : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0       
                                                               
$ cat ~/csvfile.csv                                                                                  
host, date, hostname, certificate                              
laureate-ungaikyo.mx.x.mail.umich.edu, Fri Sep 27 11:30:21 AM EDT 2024,laureate-ungaikyo.mx.x.mail.umich.edu,Dec 12 23:59:59 2024 GMT,Thu Sep 26 10:38:50 PM UTC 2024
small-jatai.mx.x.mail.umich.edu, Fri Sep 27 11:30:21 AM EDT 2024,small-jatai.mx.x.mail.umich.edu,Dec 12 23:59:59 2024 GMT,Thu Sep 26 10:38:50 PM UTC 2024
hushed-kameosa.mx.x.mail.umich.edu, Fri Sep 27 11:30:21 AM EDT 2024,hushed-kameosa.mx.x.mail.umich.edu,Dec 12 23:59:59 2024 GMT,Thu Sep 26 10:38:50 PM UTC 2024

Then you have not used the construct you suggested, got incredibly lucky, or just didn’t notice when it failed. This is not about my experience, it is an inherent flaw in how that task works.

- hosts: aws_ec2
  tasks:
    - name: Remove CSV file
      delegate_to: localhost
      ansible.builtin.file:
        path: /tmp/CSV-FILE.csv
        state: absent

    - name: Add data to CSV file
      delegate_to: localhost
      ansible.builtin.lineinfile:
        path: "/tmp/CSV-FILE.csv"
        line: "{{ tags.Name, private_ip_address, item }}"
        state: present
        create: true
      loop:
        - foo
        - bar
        - baz
$ for i in {1..10} ; do ansible-playbook test.yml &>/dev/null; wc -l /tmp/CSV-FILE.csv ; done
48 /tmp/CSV-FILE.csv
47 /tmp/CSV-FILE.csv
48 /tmp/CSV-FILE.csv
45 /tmp/CSV-FILE.csv
48 /tmp/CSV-FILE.csv
48 /tmp/CSV-FILE.csv
48 /tmp/CSV-FILE.csv
48 /tmp/CSV-FILE.csv
47 /tmp/CSV-FILE.csv
47 /tmp/CSV-FILE.csv

The argument arises because you provided conceptually broken tasks that should not be learned from, and will cause problems for people that can be subtle and hard to notice. I don’t know why you’re still arguing that it’s a safe thing to do when it’s demonstrably not, as shown in my immediately previous post. I didn’t have to try to break it or jump through hoops to get those results, I just ran it ten times and it failed on 4 of them.

Don’t write code with race conditions. It’s a bad idea.

1 Like