Creating a fact array

176 views
Skip to first unread message

lift...@gmail.com

unread,
Dec 8, 2021, 10:50:00 AM12/8/21
to Ansible Project
I had another topic about using a fact in a loop, but since this question is slightly different, I figured I'd start a new topic.

Background:
I'm using the IPA API calls using the uri module to get a list of users in our IPA server.  Once I get the uses via user_find, I set a fact of just the uid's.  This works well.  Then I am using that fact to use the user_show API via uri.  This also works, but since we have over 1600 users, it takes a bit of time.  So I'm looping through the uid list to process the first 20.

Issue:

I am registering the user_show data in a variable called user_show.  I'm trying to set a fact that has the uid, password expiration, and the age calculation.  The problem I have is that the fact has the information from the last user processed, and is not an array of data as I expect.  Here's how I'm setting the facts:

  - name: Run user_show from IDM API using previously stored session cookie
    uri:
      url: "https://{{idmfqdn}}/ipa/session/json"
      method: POST
      headers:
        Cookie: "{{ login.set_cookie }}"
        Referer: "https://{{idmfqdn}}/ipa"
        Content-Type: "application/json"
        Accept: "application/json"
      body_format: json
      body: "{\"method\": \"user_show\",\"params\": [[ \"{{ uid[item|int] }}\"],{\"all\": true,\"version\": \"{{ api_vers }}\"}]}"
    register: user_show
    with_sequence: start=0 end=19

  - name: Set user_show fact
    set_fact:
      users:
      - "{{ (user_show.results[item|int].json.result.result.krbpasswordexpiration[0]['__datetime__'] | to_datetime('%Y%m%d%H%M%SZ')).strftime('%s') }}"
      - "{{ (ansible_date_time.epoch|int - ((user_show.results[item|int].json.result.result.krbpasswordexpiration[0]['__datetime__'] | to_datetime('%Y%m%d%H%M%SZ')).strftime('%s'))|int) / (60*60*24) }}"
      - "{{ user_show.results[item|int].json.result.result.uid[0] }}"

  - name: Print users fact    <------------------- This shows the last user and not the previous 19
    debug:
      msg: "{{ users }}"

So how can I accomplish this?

Thanks,
Harry

Oluwole Leigh

unread,
Dec 8, 2021, 12:27:42 PM12/8/21
to Ansible Project
Hi there,
You can use the debug module directly to display the required output . This can be done using with_together in a  loop as shown below

debug:
  msg: "{{ item.0 }}{{ item.1 }}{{ item.2 }}"
with_together:
     - "{{ (user_show.results[item|int].json.result.result.krbpasswordexpiration[0]['__datetime__'] | to_datetime('%Y%m%d%H%M%SZ')).strftime('%s') }}"
      - "{{ (ansible_date_time.epoch|int - ((user_show.results[item|int].json.result.result.krbpasswordexpiration[0]['__datetime__'] | to_datetime('%Y%m%d%H%M%SZ')).strftime('%s'))|int) / (60*60*24) }}"
      - "{{ user_show.results[item|int].json.result.result.uid[0] }}"

If you need to store in a fact you can use the set_fact instead of debug module as seen below 

set_fact:
      users:  "{{ item.0 }}{{ item.1 }}{{ item.2 }}"
 with_together:
     - "{{ (user_show.results[item|int].json.result.result.krbpasswordexpiration[0]['__datetime__'] | to_datetime('%Y%m%d%H%M%SZ')).strftime('%s') }}"
      - "{{ (ansible_date_time.epoch|int - ((user_show.results[item|int].json.result.result.krbpasswordexpiration[0]['__datetime__'] | to_datetime('%Y%m%d%H%M%SZ')).strftime('%s'))|int) / (60*60*24) }}"
      - "{{ user_show.results[item|int].json.result.result.uid[0] }}"
 register: res

set_fact:
  userlist: "{{ item }}"
loop: "{{ res.results |map(attribute='ansible_facts.users') |list }}"

Your desired content should now be stored in the userlist variable

Wole

lift...@gmail.com

unread,
Dec 8, 2021, 12:53:41 PM12/8/21
to Ansible Project
The problem with implementing it that way is that the users's fact is referencing "user_show.results[item|int], but I can't add another loop variable.  I'm using with_sequence earlier in the playbook for testing  to limit the amount of users queried, and that number is needed for the user_show.results array.

Harry

Todd Lewis

unread,
Dec 8, 2021, 1:57:31 PM12/8/21
to ansible...@googlegroups.com

The problem with implementing it that way is that the users's fact is referencing "user_show.results[item|int], but I can't add another loop variable.  I'm using with_sequence earlier in the playbook for testing  to limit the amount of users queried, and that number is needed for the user_show.results array.

You can use loop_control to use names other than item for your loop variables. https://docs.ansible.com/ansible/latest/user_guide/playbooks_loops.html#defining-inner-and-outer-variable-names-with-loop-var

lift...@gmail.com

unread,
Dec 8, 2021, 2:08:51 PM12/8/21
to Ansible Project
So if I have this:

  - name: Set user_show fact
    set_fact:
      users: "{{ item.0 }}{{ item.1 }}{{ item.2 }}"
    with_together:

      - "{{ user_show.results[item|int].json.result.result.uid[0] }}"
      - "{{ (user_show.results[item|int].json.result.result.krbpasswordexpiration[0]['__datetime__'] | to_datetime('%Y%m%d%H%M%SZ')).strftime('%s') }}"
      - "{{ (ansible_date_time.epoch|int - ((user_show.results[item|int].json.result.result.krbpasswordexpiration[0]['__datetime__'] | to_datetime('%Y%m%d%H%M%SZ')).strftime('%s'))|int) / (60*60*24) }}"
    register: res

How do I add/use a loop_control variable to that?  Also, with_together seems to have been replaced by loop and the zip filter.

Thanks,
Harry

Oluwole Leigh

unread,
Dec 9, 2021, 12:00:49 PM12/9/21
to Ansible Project
Hi,
I wanted to know why you are using item to retrieve results  - "{{ user_show.results[item|int].json.result.result.uid[0] }}" . since item is useful when you have a loop running. Is it not possible to retrieve the result using json_query or the map filter ?If that can be done , then we won't need to bring in the loop_control variable. 
 From my experience, its better to get the basic stuff (with_X)to work first before using the latest stuff- loop/zip (filter).

lift...@gmail.com

unread,
Dec 9, 2021, 2:04:08 PM12/9/21
to Ansible Project
For my testing, I'm calling the user_show IPA API via uri for the first 5 items.  The registered variable called "user_show" has the json value of "user_show.results[X].json.result.result.Y" (where X is 0 for the first user, 1 for the second, etc; and Y is the user property (uid, firstname, etc.). I don't know how to get/retrieve just the uid property of those 5 users, or just the krbpasswordexpiration of those 5 users.  I don't have to do a loop, but nothing I try seems to let me get past the fact that the results json is an array, and I need to reference the particular items of that array.

Thanks,
Harry

Oluwole Leigh

unread,
Dec 9, 2021, 4:22:57 PM12/9/21
to ansible...@googlegroups.com
Can you show a sample of user_show.results

--
You received this message because you are subscribed to a topic in the Google Groups "Ansible Project" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/ansible-project/0fW4Hnr9sOg/unsubscribe.
To unsubscribe from this group and all its topics, send an email to ansible-proje...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/ansible-project/0785c1c0-8fe1-40d6-b1ef-e2fbd647d891n%40googlegroups.com.

lift...@gmail.com

unread,
Dec 10, 2021, 8:08:19 AM12/10/21
to Ansible Project
I'm including the playbook below, followed by the relevant output.  I have redacted the real data values with dummy values, but it should give you an idea of what I'm trying and what I'm getting.

Thanks,
Harry

Playbook:

---
- name: Gather User Password Expiration information from IDM server
  hosts: ipaserver
  gather_facts: no

  pre_tasks:
    - setup:
        filter: 'ansible_date_time'

  vars_files:
    - /etc/ansible/vault.yml

  vars:
    idmfqdn: ipaserver.example.com
    binduser: 'admin'
    bindpasswd: '{{ secure_ipa_pass }}'
    cutoff_days: 180

  tasks:

  - name: Login to IDM use returned cookie to access the API in later tasks
    uri:
      url: "https://{{idmfqdn}}/ipa/session/login_password"
      method: POST
      headers:

        Referer: "https://{{idmfqdn}}/ipa"
        Content-Type: "application/x-www-form-urlencoded"
        Accept: "text/plain"
      body_format: form-urlencoded
      body:
        user: "{{binduser}}"
        password: "{{bindpasswd}}"
      status_code: 200
    register: login

  - name: Get IDM API version using previously stored session cookie

    uri:
      url: "https://{{idmfqdn}}/ipa/session/json"
      method: POST
      headers:
        Cookie: "{{ login.set_cookie }}"
        Referer: "https://{{idmfqdn}}/ipa"
        Content-Type: "application/json"
        Accept: "application/json"
      body_format: json
      body: '{"method": "ping","params": [[],{}]}'
    register: api_vers_out

  - name: Set fact for api version
    set_fact:
      api_vers: "{{ api_vers_out.json.result.messages|json_query('[*].data.server_version')|join() }}"

  - name: Run user_find from IDM API using previously stored session cookie

    uri:
      url: "https://{{idmfqdn}}/ipa/session/json"
      method: POST
      headers:
        Cookie: "{{ login.set_cookie }}"
        Referer: "https://{{idmfqdn}}/ipa"
        Content-Type: "application/json"
        Accept: "application/json"
      body_format: json
      body: "{\"method\": \"user_find/1\",\"params\": [[],{\"version\": \"{{ api_vers }}\"}]}"
    no_log: true
    register: user_find

  - name: Set fact for users
    set_fact:
      uid: "{{ user_find.json.result.result|map(attribute='uid')|flatten }}"


  - name: Run user_show from IDM API using previously stored session cookie
    uri:
      url: "https://{{idmfqdn}}/ipa/session/json"
      method: POST
      headers:
        Cookie: "{{ login.set_cookie }}"
        Referer: "https://{{idmfqdn}}/ipa"
        Content-Type: "application/json"
        Accept: "application/json"
      body_format: json
      body: "{\"method\": \"user_show\",\"params\": [[ \"{{ uid[item|int] }}\"],{\"all\": true,\"version\": \"{{ api_vers }}\"}]}"
    register: user_show
    with_sequence: start=0 end=1

  - name: Print user_show variable
    debug:
      msg: "{{ user_show }}"


  - name: Set user_show fact
    set_fact:
      users: "{{ user_show.results|json_query('[*].json.result.result.uid[0]')|flatten }}"
      pwdexp: "{{ user_show.results|json_query('[*].json.result.result.krbpasswordexpiration[0]')|flatten }}"


  - name: Print users fact
    debug:
      msg: "{{ users }}"

  - name: Print pwdexp fact
    debug:
      msg: "{{ pwdexp }}"

  - name: Set userlist fact
    set_fact:
      userlist: "{{ item.0 }} {{ item.1 }} {{ (ansible_date_time.epoch|int - ((item.1['__datetime__'] | to_datetime('%Y%m%d%H%M%SZ')).strftime('%s'))|int) / (60*60*24) }}"
    with_together:
      - "{{ users }}"
      - "{{ pwdexp }}"

  - name: Print userlist fact
    debug:
      msg: "{{ userlist }}"

The output is as follows:

TASK [Print users fact] ***********************************************************************************************************
ok: [ipaserver.example.com] => {
    "msg": [
        "test.user1",
        "test.user2"
    ]
}

TASK [Print pwdexp fact] **********************************************************************************************************
ok: [ipaserver.example.com] => {
    "msg": [
        {
            "__datetime__": "20220103195934Z"
        },
        {
            "__datetime__": "20200218151047Z"
        }
    ]
}

TASK [Print user_show variable] ***************************************************************************************************
ok: [ipaserver.example.com] => {
    "msg": {
        "changed": false,
        "msg": "All items completed",
        "results": [
            {
                "ansible_loop_var": "item",
                "cache_control": "no-cache, private",
                "changed": false,
                "connection": "close",
                "content_length": "1681",
                "content_security_policy": "frame-ancestors 'none'",
                "content_type": "application/json; charset=utf-8",
                "cookies": {},
                "cookies_string": "",
                "date": "Fri, 10 Dec 2021 12:49:38 GMT",
                "elapsed": 0,
                "failed": false,
                "invocation": {
                    "module_args": {
                        "attributes": null,
                        "backup": null,
                        "body": {
                            "method": "user_show",
                            "params": [
                                [
                                    "test.user1"
                                ],
                                {
                                    "all": true,
                                    "version": "2.237"
                                }
                            ]
                        },
                        "body_format": "json",
                        "client_cert": null,
                        "client_key": null,
                        "content": null,
                        "creates": null,
                        "delimiter": null,
                        "dest": null,
                        "directory_mode": null,
                        "follow": false,
                        "follow_redirects": "safe",
                        "force": false,
                        "force_basic_auth": false,
                        "group": null,
                        "headers": {
                            "Accept": "application/json",
                            "Content-Type": "application/json",
                            "Cookie": "ipa_session=MagBearerToken=qaKkpuAqhB0hUfPPxewHikiD9j5wrInFi2%2b2AV%2bALxA8HHy28j5ajQUaZN%2fQj%2bnfvrZJFOYm4K9oLbw2jTWiVJ2DM1ZeKliRdy5IUCZm15DVfYmwVo8fFZQGdlamH9zS9MuuHYCx2cCkdCbaFO7UEpxceTfoa65l1Uu0KmUH4LD%2bY5ipyUnh7I2jcvviztT5wBmGWVEhTgvNSshtKoWPEg%3d%3d;path=/ipa;httponly;secure;",
                            "Referer": "https://ipaserver.example.com/ipa"
                        },
                        "http_agent": "ansible-httpget",
                        "method": "POST",
                        "mode": null,
                        "owner": null,
                        "regexp": null,
                        "remote_src": null,
                        "removes": null,
                        "return_content": false,
                        "selevel": null,
                        "serole": null,
                        "setype": null,
                        "seuser": null,
                        "src": null,
                        "status_code": [
                            200
                        ],
                        "timeout": 30,
                        "unix_socket": null,
                        "unsafe_writes": false,
                        "url": "https://ipaserver.example.com/ipa/session/json",
                        "url_password": null,
                        "url_username": null,
                        "use_proxy": true,
                        "validate_certs": true
                    }
                },
                "item": "0",
                "json": {
                    "error": null,
                    "id": null,
                    "principal": "ad...@EXAMPLE.COM",
                    "result": {
                        "result": {
                            "cn": [
                                "Test User1"
                            ],
                            "displayname": [
                                "Test User1"
                            ],
                            "dn": "uid=test.user1,cn=users,cn=accounts,dc=example,dc=com",
                            "gecos": [
                                "Test User1"
                            ],
                            "gidnumber": [
                                "10000"
                            ],
                            "givenname": [
                                "Test"
                            ],
                            "has_keytab": true,
                            "has_password": true,
                            "homedirectory": [
                                "/home/test.user1"
                            ],
                            "initials": [
                                "TU"
                            ],
                            "ipauniqueid": [
                                "8982f708-d556-11e9-8432-001a4a160181"
                            ],
                            "ipauserauthtype": [
                                "password"
                            ],
                            "krbcanonicalname": [
                                "test....@EXAMPLE.COM"
                            ],
                            "krbextradata": [
                                {
                                    "__base64__": "AAKmrlxhcm9vdC9hZG1pbkBTRUNVUkUtT1NFLkZBQS5HT1YA"
                                }
                            ],
                            "krblastfailedauth": [
                                {
                                    "__datetime__": "20211005195828Z"
                                }
                            ],
                            "krblastpwdchange": [
                                {
                                    "__datetime__": "20211005195934Z"
                                }
                            ],
                            "krblastsuccessfulauth": [
                                {
                                    "__datetime__": "20211122212918Z"
                                }
                            ],
                            "krbloginfailedcount": [
                                "0"
                            ],
                            "krbpasswordexpiration": [
                                {
                                    "__datetime__": "20220103195934Z"
                                }
                            ],
                            "krbprincipalname": [
                                "test....@EXAMPLE.COM"
                            ],
                            "loginshell": [
                                "/bin/bash"
                            ],
                            "mail": [
                                "test....@example.com"
                            ],
                            "memberof_group": [
                                "ipausers"
                            ],
                            "memberofindirect_group": [
                                "folderaccess"
                            ],
                            "nsaccountlock": false,
                            "objectclass": [
                                "ipasshgroupofpubkeys",
                                "krbticketpolicyaux",
                                "ipaobject",
                                "organizationalperson",
                                "top",
                                "ipasshuser",
                                "inetorgperson",
                                "person",
                                "ipauserauthtypeclass",
                                "inetuser",
                                "krbprincipalaux",
                                "posixaccount"
                            ],
                            "preserved": false,
                            "sn": [
                                "User"
                            ],
                            "telephonenumber": [
                                "(xxx) yyy-zzzz"
                            ],
                            "uid": [
                                "test.user1"
                            ],
                            "uidnumber": [
                                "1000"
                            ]
                        },
                        "summary": null,
                        "value": "test.user1"
                    },
                    "version": "4.6.8"
                },
                "msg": "OK (1681 bytes)",
                "redirected": false,
                "server": "Apache/2.4.6 (Red Hat Enterprise Linux) mod_auth_gssapi/1.5.1 mod_nss/1.0.14 NSS/3.28.4 mod_wsgi/3.4 Python/2.7.5",
                "status": 200,
                "url": "https://ipaserver.example.com/ipa/session/json",
                "vary": "Accept-Encoding",
                "x_frame_options": "DENY"
            },
            {
                "ansible_loop_var": "item",
                "cache_control": "no-cache, private",
                "changed": false,
                "connection": "close",
                "content_length": "1368",
                "content_security_policy": "frame-ancestors 'none'",
                "content_type": "application/json; charset=utf-8",
                "cookies": {},
                "cookies_string": "",
                "date": "Fri, 10 Dec 2021 12:49:40 GMT",
                "elapsed": 0,
                "failed": false,
                "invocation": {
                    "module_args": {
                        "attributes": null,
                        "backup": null,
                        "body": {
                            "method": "user_show",
                            "params": [
                                [
                                    "test.user2"
                                ],
                                {
                                    "all": true,
                                    "version": "2.237"
                                }
                            ]
                        },
                        "body_format": "json",
                        "client_cert": null,
                        "client_key": null,
                        "content": null,
                        "creates": null,
                        "delimiter": null,
                        "dest": null,
                        "directory_mode": null,
                        "follow": false,
                        "follow_redirects": "safe",
                        "force": false,
                        "force_basic_auth": false,
                        "group": null,
                        "headers": {
                            "Accept": "application/json",
                            "Content-Type": "application/json",
                            "Cookie": "ipa_session=MagBearerToken=qaKkpuAqhB0hUfPPxewHikiD9j5wrInFi2%2b2AV%2bALxA8HHy28j5ajQUaZN%2fQj%2bnfvrZJFOYm4K9oLbw2jTWiVJ2DM1ZeKliRdy5IUCZm15DVfYmwVo8fFZQGdlamH9zS9MuuHYCx2cCkdCbaFO7UEpxceTfoa65l1Uu0KmUH4LD%2bY5ipyUnh7I2jcvviztT5wBmGWVEhTgvNSshtKoWPEg%3d%3d;path=/ipa;httponly;secure;",
                            "Referer": "https://ipaserver.example.com/ipa"
                        },
                        "http_agent": "ansible-httpget",
                        "method": "POST",
                        "mode": null,
                        "owner": null,
                        "regexp": null,
                        "remote_src": null,
                        "removes": null,
                        "return_content": false,
                        "selevel": null,
                        "serole": null,
                        "setype": null,
                        "seuser": null,
                        "src": null,
                        "status_code": [
                            200
                        ],
                        "timeout": 30,
                        "unix_socket": null,
                        "unsafe_writes": false,
                        "url": "https://ipaserver.example.com/ipa/session/json",
                        "url_password": null,
                        "url_username": null,
                        "use_proxy": true,
                        "validate_certs": true
                    }
                },
                "item": "1",
                "json": {
                    "error": null,
                    "id": null,
                    "principal": "ad...@EXAMPLE.COM",
                    "result": {
                        "result": {
                            "cn": [
                                "Test User2"
                            ],
                            "displayname": [
                                "Test User2"
                            ],
                            "dn": "uid=test.user2,cn=users,cn=accounts,dc=example,dc=com",
                            "gecos": [
                                "Test User2"
                            ],
                            "gidnumber": [
                                "10000"
                            ],
                            "givenname": [
                                "Test"
                            ],
                            "has_keytab": true,
                            "has_password": true,
                            "homedirectory": [
                                "/home/test.user2"
                            ],
                            "initials": [
                                "TU"
                            ],
                            "ipauniqueid": [
                                "60fe8b86-5282-11ea-ac93-001a4a160181"
                            ],
                            "krbcanonicalname": [
                                "test....@EXAMPLE.COM"
                            ],
                            "krbextradata": [
                                {
                                    "__base64__": "AAK6Nkxecm9vdC9hZG1pbkBTRUNVUkUtT1NFLkZBQS5HT1YA"
                                }
                            ],
                            "krbpasswordexpiration": [
                                {
                                    "__datetime__": "20200218151047Z"
                                }
                            ],
                            "krbprincipalname": [
                                "test....@EXAMPLE.COM"
                            ],
                            "loginshell": [
                                "/bin/bash"
                            ],
                            "mail": [
                                "test....@example.com"
                            ],
                            "memberof_group": [
                                "ipausers"
                            ],
                            "memberofindirect_group": [
                                "folderaccess"
                            ],
                            "nsaccountlock": true,
                            "objectclass": [
                                "top",
                                "person",
                                "organizationalperson",
                                "inetorgperson",
                                "inetuser",
                                "posixaccount",
                                "krbprincipalaux",
                                "krbticketpolicyaux",
                                "ipaobject",
                                "ipasshuser",
                                "ipaSshGroupOfPubKeys"
                            ],
                            "preserved": false,
                            "sn": [
                                "User"
                            ],
                            "telephonenumber": [
                                "(xxx)yyy-zzzz"
                            ],
                            "uid": [
                                "test.user2"
                            ],
                            "uidnumber": [
                                "1100"
                            ]
                        },
                        "summary": null,
                        "value": "test.user2"
                    },
                    "version": "4.6.8"
                },
                "msg": "OK (1368 bytes)",
                "redirected": false,
                "server": "Apache/2.4.6 (Red Hat Enterprise Linux) mod_auth_gssapi/1.5.1 mod_nss/1.0.14 NSS/3.28.4 mod_wsgi/3.4 Python/2.7.5",
                "status": 200,
                "url": "https://ipaserver.example.com/ipa/session/json",
                "vary": "Accept-Encoding",
                "x_frame_options": "DENY"
            }
        ]
    }
}

TASK [Set userlist fact] **********************************************************************************************************
ok: [ipaserver.example.com] => (item=[u'test.user1', {u'__datetime__': u'20220103195934Z'}])
ok: [ipaserver.example.com] => (item=[u'test.user2', {u'__datetime__': u'20200218151047Z'}])

TASK [Print userlist fact] ********************************************************************************************************
ok: [ipaserver.example.com] => {
    "msg": "test.user2 {u'__datetime__': u'20200218151047Z'} 660.693483796"
}

PLAY RECAP ************************************************************************************************************************
ipaserver.example.com   : ok=12   changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Oluwole Leigh

unread,
Dec 10, 2021, 11:39:35 AM12/10/21
to ansible...@googlegroups.com
Hi there,
I created this sample playbook and based on the data you sent.
I used the mail field because that was not embedded in another key/value pair like krbpasswordexpiration but the principle is basically the same.
The difference with your playbook is that I used a variable to register the set_fact . Without that, it would just show the value of the last item in the loop which will overwrite the previous values. By registering the output , I can extract out all the values in the last task. 
There are probably other methods but this one works. 

  tasks:
    - name: lookup
      set_fact:
        fil: "{{ lookup('file', 'json.json') }}"
    - name: set
      set_fact:
        users: "{{ fil |json_query('[*].json.result.result.uid')|flatten }}"
        pwdexp: "{{ fil |json_query('[*].json.result.result.mail')|flatten }}"
    - name: sf1
      set_fact:
        factone: "{{ item.0 }} {{ item.1 }}"

      with_together:
         - "{{ users }}"
         - "{{ pwdexp }}"
      register: facttwo
    - name: sf2
      set_fact:
        factthree: "{{ facttwo.results | map(attribute='ansible_facts.factone')|list }}"
    - name: debug
      debug:
        msg: "{{ factthree }}"
                               
 
The Output is displayed below


ASK [sf1] *********************************************************************************************************************************
ok: [localhost] => (item=['test.user1', 'test....@example.com'])
ok: [localhost] => (item=['test.user2', 'test....@example.com'])

TASK [sf2] *********************************************************************************************************************************
ok: [localhost]

TASK [debug] *******************************************************************************************************************************
ok: [localhost] => {
    "msg": [
        "test.user1 test....@example.com",
        "test.user2 test....@example.com"
    ]
}



Todd Lewis

unread,
Dec 10, 2021, 2:52:03 PM12/10/21
to Ansible Project
I finally installed FreeIPA in a VM on my laptop so I could see what you're seeing. Except for having a self-signed certificate and way less than 1500 users, I think I've got something I can test on. A few things to note, though they may not be all that you're looking for:
  • Exclude your 'admin' user. It shouldn't be an issue, but, just in case. :)
  • I used loop: "{{ uid | json_query('[:20]') }}" to limit the 'user_show' calls rather than with_sequence:. Not better or worse, just showing another way to do it. Since we're both using json_query later, it has to be available anyway.
  • Once you've got your show_user output, you can go straight to a list of users to delete - with their pw expiration dates - in one json_query. There's no need to create so many intermediate set_fact lists.
I didn't notice before, but you aren't doing nested loops after all, so all that stuff I mentioned about loop_control is irrelevant. Sorry about the rabbit hole.

Here's my current playbook to produce a list like
  - uid: user1
    pwdepx: 20201210012032Z
  - uid: user2
    pwdepx: 20181080021425Z
  ...
of just the users who are eligible for deletion. Hope this helps.

  - name: Set fact for users
    set_fact:
      uid: "{{ user_find.json.result.result|map(attribute='uid')|flatten|difference(['admin']) }}"


  - name: Run user_show from IDM API using previously stored session cookie
    uri:
      url: "https://{{idmfqdn}}/ipa/session/json"
      validate_certs: "{{ validate_certs }}"

      method: POST
      headers:
        Cookie: "{{ login.set_cookie }}"
        Referer: "https://{{idmfqdn}}/ipa"
        Content-Type: "application/json"
        Accept: "application/json"
      body_format: json
      body:
        method: user_show
        params:
          - - "{{ item }}"
          - all: true
            version: "{{ api_vers }}"
    register: user_show
    loop: "{{ uid | json_query('[:20]') }}"


  - name: Print user_show variable
    debug:
      msg: "{{ user_show }}"

  - name: Save "{{ cutoff_days }} days ago" in krbpasswordexpiration.__datetime__ format
    set_fact:
      cutoff_date: '{{ lookup(''pipe'',
                     ''date -u --date="{{ cutoff_days }} days ago" +%Y%m%d000000Z'') }}'

  - name: Set deletable_users fact
    set_fact:
      deletable_users: "{{ user_show.results |
        json_query('[*].json.result.result.{uid: uid[0], pwdexp: krbpasswordexpiration[0].__datetime__}') |
        selectattr('pwdexp','<',cutoff_date) | list }}"

  - name: Print deletable_users
    debug:
      msg: "{{ deletable_users }}"

lift...@gmail.com

unread,
Dec 10, 2021, 2:56:26 PM12/10/21
to Ansible Project

OK, I got that to work following your example.  Thanks for that.  So the next step I'm trying to get to is to determine if the user's password has been expired for more than 180 days.  A negative value means its not expired, and a positive value means that it is expired.  I know you used email, but I'm using password expiration (krbpasswordexpiration), and I am actually calculating the password age in the fact at the same time.  So now the playbook looks like this:

  - name: Grab UID/Password Expiration/Disabled Status from user_show

    set_fact:
      users: "{{ user_show.results|json_query('[*].json.result.result.uid[0]')|flatten }}"
      pwdexp: "{{ user_show.results|json_query('[*].json.result.result.krbpasswordexpiration[0]')|flatten }}"

  - name: Combine data into one fact
    set_fact:
      tmpuserlist1: "{{ item.0 }} {{ (ansible_date_time.epoch|int - ((item.1['__datetime__'] | to_datetime('%Y%m%d%H%M%SZ')).strftime('%s'))|int) / (60*60*24) }}"

    with_together:
      - "{{ users }}"
      - "{{ pwdexp }}"
    register: tmpuserlist2

  - name: Set Temporary User List fact
    set_fact:
      userlist: "{{ tmpuserlist2.results|map(attribute='ansible_facts.tmpuserlist1')|list }}"

  - name: Print Final User List
    debug:
      msg: "{{ userlist }}"

Which gives the following:

TASK [Grab UID/Password Expiration/Disabled Status from user_show] ****************************************************************
ok: [ipaserver.example.com]

TASK [Combine data into one fact] *************************************************************************************************

ok: [ipaserver.example.com] => (item=[u'test.user1', {u'__datetime__': u'20220103195934Z'}])
ok: [ipaserver.example.com] => (item=[u'test.user2', {u'__datetime__': u'20200218151047Z'}])

TASK [Set Temporary User List fact] ***********************************************************************************************
ok: [ipaserver.example.com]

TASK [Print Final User List] ******************************************************************************************************
ok: [ipaserver.example.com] => {
    "msg": [
        "test.user1 -24.2161574074",
        "test.user2 660.984386574"
    ]
}

I think that the next step should be to create a list of users who's age is greater than or equal to 180, then go loop through that list and disable them.  But I can't seem to figure out how to grab the age field now that it's in the new "userlist" fact.  If I try to show each item individually via a loop like this:

  - name: Print Final User List
    debug:
      msg: "{{ item }}"
    loop: "{{ userlist }}"

I get:

TASK [Print Final User List] ******************************************************************************************************
ok: [ipaserver.example.com] => (item=test.user1 -24.2126273148) => {
    "msg": "test.user1 -24.2126273148"
}
ok: [ipaserver.example.com] => (item=test.user2 660.987916667) => {
    "msg": "test.user2 660.987916667"
}

Should I be using the tmpuserlist2 fact that was generated using "with_together" instead for this loop/process?

Thanks,
Harry
On Friday, December 10, 2021 at 11:39:35 AM UTC-5 wolei...@gmail.com wrote:

lift...@gmail.com

unread,
Dec 10, 2021, 3:38:34 PM12/10/21
to Ansible Project
So a few things that I tried based on Uto's suggestion:  I changed the cutoff days to 30 for testing, and I only look over the first 10 users.  I had to change the < check to >, but when the disabled_users fact is printed, It shows the same user all 10 times, almost as if it added the user 10 times.  Any ideas?

Harry

  - name: Run user_show from IDM API using previously stored session cookie
    uri:
      url: "https://{{idmfqdn}}/ipa/session/json"
      method: POST
      headers:
        Cookie: "{{ login.set_cookie }}"
        Referer: "https://{{idmfqdn}}/ipa"
        Content-Type: "application/json"
        Accept: "application/json"
      body_format: json
      body: "{\"method\": \"user_show\",\"params\": [[ \"{{ uid[item|int] }}\"],{\"all\": true,\"version\": \"{{ api_vers }}\"}]}"
    register: user_show
    loop: "{{ uid|json_query('[:10]') }}"

  - name: Save "{{ cutoff_days }}" in Password Expiration format

    set_fact:
      cutoff_date: '{{ lookup(''pipe'', ''date -u --date="{{ cutoff_days }} days ago" +%Y%m%d000000Z'') }}'

  - name: Print cutoff_date
    debug:
      msg: "{{ cutoff_date }}"

  - name: Set Disabled Users fact
    set_fact:
      disabled_users: "{{ user_show.results | json_query('[*].json.result.result.{uid: uid[0], pwdexp: krbpasswordexpiration[0].__datetime__}') | selectattr('pwdexp','>',cutoff_date) | list }}"

  - name: Print disabled users
    debug:
      msg: "{{ disabled_users }}"

Output:

TASK [Print disabled users] *******************************************************************************************************
ok: [ipaserver.example.com] => {
    "msg": [
        {
            "pwdexp": "20220103195934Z",
            "uid": "test.user1"
        },
        {
            "pwdexp": "20220103195934Z",
            "uid": "test.user1"
        },
        {
            "pwdexp": "20220103195934Z",
            "uid": "test.user1"
        },
        {
            "pwdexp": "20220103195934Z",
            "uid": "test.user1"
        },
        {
            "pwdexp": "20220103195934Z",
            "uid": "test.user1"
        },
        {
            "pwdexp": "20220103195934Z",
            "uid": "test.user1"
        },
        {
            "pwdexp": "20220103195934Z",
            "uid": "test.user1"
        },
        {
            "pwdexp": "20220103195934Z",
            "uid": "test.user1"
        },
        {
            "pwdexp": "20220103195934Z",
            "uid": "test.user1"
        },
        {
            "pwdexp": "20220103195934Z",
            "uid": "test.user1"
        }
    ]
}

Todd Lewis

unread,
Dec 10, 2021, 3:49:56 PM12/10/21
to ansible...@googlegroups.com

> So a few things that I tried based on Uto's suggestion:  I changed the
> cutoff days to 30 for testing, and I only look over the first 10
> users.  I had to change the < check to >, but when the disabled_users
> fact is printed, It shows the same user all 10 times, almost as if it
> added the user 10 times.  Any ideas?
That is strange. I'll go create some more test users and back-date their
pw expiration dates. Hmm.

Todd Lewis

unread,
Dec 10, 2021, 4:40:15 PM12/10/21
to Ansible Project
When you change from  with_sequence to loop: "{{ uid|json_query('[:10]') }}", item is no longer an index into the uid list. It's actual uids. So your body becomes
      body:
        method: user_show
        params:
          - - "{{ item }}"
          - all: true
            version: "{{ api_vers }}"
    register: user_show
    loop: "{{ uid | json_query('[:20]') }}"

(And you don't have to generate the json string yourself. The module will do that for you. Much easier to read/maintain than
    body: "{\"method\": \"user_show\",\"params\": [[ \"{{ item }}\"],{\"all\": true,\"version\": \"{{ api_vers }}\"}]}"
but I fixed that line just in case you like that better.)

Also, change the '>' back to '<'. Earlier date strings are '<' later date strings.

lift...@gmail.com

unread,
Dec 10, 2021, 8:45:30 PM12/10/21
to Ansible Project
Todd,

Thank you a MILLION!  Got it to work, and its much simpler too.  I can't thank you enough for going above and beyond the call in helping me out.  I'll keep working on it next week, but for now its doing exactly what I need it to do at this point.  And I picked up a few tricks along the way, like the json_query('[:20]') and how to use the difference filter to ignore user names I don't need to check.  Great stuff here!

Thanks!
Harry

lift...@gmail.com

unread,
Dec 13, 2021, 9:33:13 AM12/13/21
to Ansible Project
I have another quick question:  I got this to work and it currently shows ALL disabled accounts.  I can also grab the nsaccountlock value, which will show True if the account is already disabled and False if it is not.  What'd I'd like to do is filter the results to users who's password has been expired 180 days or longer (selectattr('pwdexp', "<",cutoff_date) AND nsaccountlock is false.  How do I specify that in the example you gave using selectattr? My "disabled users" fact is currently:

  - name: Set Disabled Users fact
    set_fact:
      disabled_users: "{{ user_show.results | json_query('[*].json.result.result.{uid: uid[0], mail: mail[0], disabled: nsaccountlock, pwdexp: krbpasswordexpiration[0].__datetime__}') | selectattr('pwdexp','<',cutoff_date) | list }}"



  - name: Print disabled users
    debug:
      msg: "{{ item.uid }} / {{ item.mail }} / {{ item.disabled |ternary('Disabled', 'Not Disabled') }} / {{ item.pwdexp }}"
    loop: "{{ disabled_users }}"

Thanks,
Harry

Oluwole Leigh

unread,
Dec 13, 2021, 12:14:29 PM12/13/21
to Ansible Project

Hi there,
Have you tried using selectattr('nsaccountlock', 'true')  if the value is logical (True / false) as an extra filter in the set_fact task?
Also a long route could be to compare deletable_users with disabled_users and use the intersection filter
{{ deletable_users | intersect(disabled_users) }}

Todd Lewis

unread,
Dec 13, 2021, 4:29:00 PM12/13/21
to Ansible Project
  - name: Set Disabled Users fact
    set_fact:
      disabled_users: "{{ user_show.results | json_query('[*].json.result.result.{uid: uid[0], mail: mail[0], disabled: nsaccountlock, pwdexp: krbpasswordexpiration[0].__datetime__}') |
         selectattr('pwdexp', '<', cutoff_date) |
         selectattr('disabled', '==', false) | list }}"


On Monday, December 13, 2021 at 9:33:13 AM UTC-5 lift...@gmail.com wrote:

lift...@gmail.com

unread,
Dec 14, 2021, 10:58:12 AM12/14/21
to Ansible Project
I used "selectattr('disabled','equalto',False)", but it gave me the desired result.  Thanks, I appreciate it!

Harry

Reply all
Reply to author
Forward
0 new messages