# Accessing contents of a fact in a loop

**URL:** <https://forum.ansible.com/t/accessing-contents-of-a-fact-in-a-loop/36043>\
**Category:** Ansible Project\
**Created:** [November 29, 2021, 8:05pm UTC](https://forum.ansible.com/t/accessing-contents-of-a-fact-in-a-loop/36043 "2021-11-29T20:05:37Z")\
**Posts on this page:** 13\
**Page:** 1

<div class="post-metadata">

**Author:** ![harry\_devine](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/harry_devine/32/5293_2.png) [@harry\_devine](https://forum.ansible.com/u/harry_devine)\
**Post date:** [November 29, 2021, 8:05pm UTC](https://forum.ansible.com/t/accessing-contents-of-a-fact-in-a-loop/36043/1 "2021-11-29T20:05:37Z")

</div>

I am traversing our IPA server to get find all users, then I want to loop through all of them to get their password expiration date. I use the IPA API via the uri module and register the variable, but no matter what I try to access the uid of each found user, I get the following error:

TASK [Run user\_show from IDM API using previously stored session cookie] \*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*  
fatal: [localhost]: FAILED! =\> {“msg”: “template error while templating string: expected name or number. String: {"method": "user\_show","params": [["{{ item[0].[‘uid’] }}"],{"all": true,"version": "{{ api\_vers }}"}]}”}

Here’s the section of my playbook that seems to be giving me issues:

- 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 }}"}]}”  
register: user\_find

- 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": [["{{ item[0].[‘uid’] }}"],{"all": true,"version": "{{ api\_vers }}"}]}”  
register: user\_show  
loop:

- “{{ user\_find.json.result.result }}”

Thanks,  
Harry

---

<div class="post-metadata">

**Author:** ![utoddl](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/utoddl/32/249_2.png) [@utoddl](https://forum.ansible.com/u/utoddl)\
**Post date:** [November 29, 2021, 8:45pm UTC](https://forum.ansible.com/t/accessing-contents-of-a-fact-in-a-loop/36043/2 "2021-11-29T20:45:46Z")

</div>

Before the step that’s failing, insert a debug step with the msg: “{{ user\_find.json.result.result }}” (really? “result.result”? maybe…) so you (and we) can be certain what your items actually look like. Otherwise, we’re just guessing.

---

<div class="post-metadata">

**Author:** ![harry\_devine](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/harry_devine/32/5293_2.png) [@harry\_devine](https://forum.ansible.com/u/harry_devine)\
**Post date:** [November 30, 2021, 1:26pm UTC](https://forum.ansible.com/t/accessing-contents-of-a-fact-in-a-loop/36043/3 "2021-11-30T13:26:47Z")

</div>

I had done that previously and knew it was getting the right data, but I put the debug back in and the redacted output is below. There are over 1600 users, so I am only showing the start of the data in a redacted form. The debug print is printing “{{ user\_find.json.result.result }}”:

TASK [Print users found] \*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*  
ok: [[auth1.secure-ose.faa.gov](http://auth1.secure-ose.faa.gov)] =\> {  
“msg”: [  
{  
“dn”: “uid=harry.devine,cn=users,cn=accounts,dc=example,dc=com”,  
“gidnumber”: [  
“11111”  
],  
“givenname”: [  
“Harry”  
],  
“homedirectory”: [  
“/home/harry.devine”  
],  
“krbcanonicalname”: [  
“[harry.devine@EXAMPLE.COM](mailto:harry.devine@EXAMPLE.COM)”  
],  
“krbprincipalname”: [  
“[harry.devine@EXAMPLE.COM](mailto:harry.devine@EXAMPLE.COM)”  
],  
“loginshell”: [  
“/bin/bash”  
],  
“mail”: [  
“[harry.devine@example.com](mailto:harry.devine@example.com)”  
],  
“nsaccountlock”: false,  
“sn”: [  
“Devine”  
],  
“telephonenumber”: [  
“(800) 867-5309”  
],  
“uid”: [  
“harry.devine”  
],  
“uidnumber”: [  
“1111”  
]  
},

Thanks,  
Harry

---

<div class="post-metadata">

**Author:** ![harry\_devine](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/harry_devine/32/5293_2.png) [@harry\_devine](https://forum.ansible.com/u/harry_devine)\
**Post date:** [December 3, 2021, 5:01pm UTC](https://forum.ansible.com/t/accessing-contents-of-a-fact-in-a-loop/36043/4 "2021-12-03T17:01:13Z")

</div>

So I’m still trying to get this to work. I’m thinking that the fact is one large item, so I need to know how I can loop through those items. I’m trying to get the UID of each user. What the user\_find IPA API call returns is .json.result.result, and the users are added to the fact in the following form:

- [user1]\n
- [user2]\n

I’m setting the user\_find variable and fact as follows:

- 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|json\_query(‘[\*].uid’) | list | to\_yaml }}”

The user\_find information is listed earlier in this thread. So I’m trying to got through that variable and pull out each UID. Without the to\_yaml filter, those are shown as [“user1”], [“user2”], etc. So how do I loop through these? Can I set up the fact as an array of user IDs and loop through that? If so, how?

Thanks,  
Harry

---

<div class="post-metadata">

**Author:** ![utoddl](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/utoddl/32/249_2.png) [@utoddl](https://forum.ansible.com/u/utoddl)\
**Post date:** [December 3, 2021, 9:17pm UTC](https://forum.ansible.com/t/accessing-contents-of-a-fact-in-a-loop/36043/5 "2021-12-03T21:17:14Z")

</div>

I really want to love Ansible, but the fact that such a simple data manipulation completely eludes the newbie doesn’t help. Worse, that I’ve done this (or equivalent) dozens of times and it still takes me as long as it does to come up with a working demo … [sigh].

Anyway, here’s a working demo, assuming I got your initial data shaped right. Good luck.

---

<div class="post-metadata">

**Author:** ![harry\_devine](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/harry_devine/32/5293_2.png) [@harry\_devine](https://forum.ansible.com/u/harry_devine)\
**Post date:** [December 3, 2021, 9:32pm UTC](https://forum.ansible.com/t/accessing-contents-of-a-fact-in-a-loop/36043/6 "2021-12-03T21:32:01Z")

</div>

That works, but when I try to then call the IPA user\_show API, which takes the UID as a parameter, the entire list generated is sent in.

- 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": [["{{ item }}"],{"all": true,"version": "{{ api\_vers }}"}]}”  
register: user\_show  
loop:
- “{{ uid }}”

TASK [Run user\_show from IDM API using previously stored session cookie] \*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*  
ok: [localhost] =\> (item=[u’user1’, u’user2’, u’user3’])

“invocation”: {  
“module\_args”: {  
“attributes”: null,  
“backup”: null,  
“body”: {  
“method”: “user\_show”,  
“params”: [  
[  
“[u’user1’, u’user2’, u’user3’]”  
],  
{  
“all”: true,  
“version”: “2.237”  
}  
]  
},

“message”: "[u’user1’, u’user2’, u’user3’]: user not found

So, why does the debug print appear to print each UID out, but when I try to reference them in the loop, they are sent over as 1 big string?

Thanks,  
Harry

---

<div class="post-metadata">

**Author:** ![utoddl](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/utoddl/32/249_2.png) [@utoddl](https://forum.ansible.com/u/utoddl)\
**Post date:** [December 3, 2021, 10:08pm UTC](https://forum.ansible.com/t/accessing-contents-of-a-fact-in-a-loop/36043/7 "2021-12-03T22:08:47Z")

</div>

I don’t see where you’re setting uid, the debug step, or its output. All I see is that

loop:

- “{{ uid }}”

is only producing one invocation of the task with the desired values all glommed into one string.  
Please show your input code and resulting output. I suspect you’re somehow producing a string rather than a list, but with nothing to look at, it’s hard to know.

---

<div class="post-metadata">

**Author:** ![harry\_devine](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/harry_devine/32/5293_2.png) [@harry\_devine](https://forum.ansible.com/u/harry_devine)\
**Post date:** [December 4, 2021, 12:02am UTC](https://forum.ansible.com/t/accessing-contents-of-a-fact-in-a-loop/36043/8 "2021-12-04T00:02:41Z")

</div>

I set it as a fact using your suggestion:  
user\_find.json.result.result|map(attribute=‘uid’)|flatten

Harry

---

<div class="post-metadata">

**Author:** ![csmart](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/csmart/32/562_2.png) [@csmart](https://forum.ansible.com/u/csmart)\
**Post date:** [December 4, 2021, 12:27am UTC](https://forum.ansible.com/t/accessing-contents-of-a-fact-in-a-loop/36043/9 "2021-12-04T00:27:00Z")

</div>

Hi Harry, as Todd mentioned it looks like you might be expecting uid there (and user\_find.json.result.result from your first example) to be a list? (It’s printing like one.)

If so, i could be wrong but you probably don’t need to reference the variable inside a list like this:

loop:

- “{{ uid }}”

Just reference the variable directly like this (as it’s already a list).

loop: “{{ uid }}”

Otherwise Ansible is looping through the list and rendering the first entry as a string (but you want that to be the list).

For example, here’s a playbook crafted similarly to yours:

---

<div class="post-metadata">

**Author:** ![utoddl](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/utoddl/32/249_2.png) [@utoddl](https://forum.ansible.com/u/utoddl)\
**Post date:** [December 4, 2021, 12:27am UTC](https://forum.ansible.com/t/accessing-contents-of-a-fact-in-a-loop/36043/10 "2021-12-04T00:27:11Z")

</div>

You left out the rest of your set\_fact step.  
Please include the whole step. Thanks.

---

<div class="post-metadata">

**Author:** ![harry\_devine](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/harry_devine/32/5293_2.png) [@harry\_devine](https://forum.ansible.com/u/harry_devine)\
**Post date:** [December 7, 2021, 4:24pm UTC](https://forum.ansible.com/t/accessing-contents-of-a-fact-in-a-loop/36043/11 "2021-12-07T16:24:35Z")

</div>

OK, so that works, but I’m still having issues with referencing. So I’m using user\_find from the IPA API using the uri module. I get the user account info correctly as follows:

user\_find.json.result.result returns:

“result”: [  
{  
“dn”: “uid=harry.devine,cn=users,cn=accounts,dc=example,dc=com”,  
“gidnumber”: [  
“10000”  
],  
“givenname”: [  
“Harry”  
],  
“homedirectory”: [  
“/home/harry.devine”  
],  
“krbcanonicalname”: [  
“[harry.devine@EXAMPLE.COM](mailto:harry.devine@EXAMPLE.COM)”  
],  
“krbprincipalname”: [  
“[harry.devine@EXAMPLE.COM](mailto:harry.devine@EXAMPLE.COM)”  
],  
“loginshell”: [  
“/bin/bash”  
],  
“mail”: [  
“[harry.devine@example.com](mailto:harry.devine@example.com)”  
],  
“nsaccountlock”: false,  
“sn”: [  
“Devine”  
],  
“telephonenumber”: [  
“(xxx)yyy-zzzz”  
],  
“uid”: [  
“harry.devine”  
],  
“uidnumber”: [  
“1111”  
]  
}

I then set the fact to pull out the user ID:

- name: Set fact for users  
set\_fact:  
uid: “{{ user\_find.json.result.result|map(attribute=‘uid’)|flatten }}”

then use that fact into the url module using the IPA API user\_show. When I print out user\_show, I get the following (I left out most of the user information as it’s redundant):

“krbpasswordexpiration”: [  
{  
“ **datetime** ”: “20220220212310Z”  
}

So when I print out the password expiration, I can reference it using user\_show.results[0].json.result.result.krbpasswordexpiration[0][‘ **datetime** ’]. But when I try to set a fact with that information, I get an error that says that krbpasswordexpiration doesn’t exist. Here’s that set\_fact:

- name: Set fact for password expirations  
set\_fact:  
pwdexpires: “{{ user\_show.results[0].json.result.result|map(attribute=‘krbpasswordexpiration’) | flatten }}”

What I’m hoping to get to is:

1. Find all users and set the uid fact
2. Loop through those uid values and call user\_show so I can retrieve each user’s password expiration
3. Determine if their password has expired more than 180 days
4. Create a list of users to disable
5. Loop through that list and disable each user
6. Email each user to inform them of the disable

So I have 1 and 2 working, but transitioning to 3 using both facts (uid and pwdexpires) is what’s giving me trouble. Any thoughts/ideas on how to accomplish the retrieval of the password expiration and have it in a fact? Or, maybe the better question is: can I have a fact with more than one value in it: 1 for uid and 1 for password expiration? I already know the uid via the result of user\_show, so I should be able to pull out both values, but how?

Thanks, and sorry for the long-winded explanation. Just trying to be as thorough and complete with you all.  
Harry

---

<div class="post-metadata">

**Author:** ![utoddl](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/utoddl/32/249_2.png) [@utoddl](https://forum.ansible.com/u/utoddl)\
**Post date:** [December 7, 2021, 5:05pm UTC](https://forum.ansible.com/t/accessing-contents-of-a-fact-in-a-loop/36043/12 "2021-12-07T17:05:54Z")

</div>

> When I print out user\_show…

Can you please show your step where you print out `user_show` (presumably a debug step), and all relevant parts of its output. “Relevant parts” would include any `[` or `{` characters, i.e. anything showing the context of “krbpasswordexpiration” in the registered data, as well as the step header (host and asterisks). Thanks.

---

<div class="post-metadata">

**Author:** ![harry\_devine](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/harry_devine/32/5293_2.png) [@harry\_devine](https://forum.ansible.com/u/harry_devine)\
**Post date:** [December 7, 2021, 6:21pm UTC](https://forum.ansible.com/t/accessing-contents-of-a-fact-in-a-loop/36043/13 "2021-12-07T18:21:58Z")

</div>

So after I sent that, i did some more playing around, and I think I have what I need for now. I’m setting multiple facts in 1 fact as follows:

- name: Set user\_show fact  
set\_fact:  
users:

- “{{ user\_show.results[0].json.result.result.uid[0] }}”

- “{{ (user\_show.results[0].json.result.result.krbpasswordexpiration[0][‘ **datetime** ’] | to\_datetime(‘%Y%m%d%H%M%SZ’)).strftime(‘%s’) }}”

- “{{ (ansible\_date\_time.epoch|int - ((user\_show.results[0].json.result.result.krbpasswordexpiration[0][‘ **datetime** ’] | to\_datetime(‘%Y%m%d%H%M%SZ’)).strftime(‘%s’))|int) / (60_60_24) }}”

- name: Print users fact  
debug:  
msg: “{{ users[0] }} : {{ users[1] }} : age={{ users[2] }}”

When the playbook runs, I get:

TASK [Print users fact] \*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*  
ok: [[auth1.secure-ose.faa.gov](http://auth1.secure-ose.faa.gov)] =\> {  
“msg”: “harry.devine : 1645410190 : age=-75.3984606481”  
}

I think from here I should be able to loop through that list, and start making a new fact list for anyone who’s password age is \>= 180.

Thanks,  
Harry
