Access expiration date for a user

I’m using the freeipa APIs in a playbook to get a user’s LDAP information. I can get the krbpasswordexpiration fine:

“krbpasswordexpiration”: [
{
datetime”: “20220207191401Z”
}

But I can’t figure out how to access the date string to convert it to a date object. I need it in the form of YYMMDD. If I try to set a fact as follows:

user_show.json.result.result.krbpasswordexpiration

I get an error about the fact being a list and not a string. How do I pull out the date string so I can turn it into a date?

What I’m ultimately trying to do is grab a user’s expiration date, and compare it to the current date. If the date is greater than or equal to 180, I need to disable the account.

Thanks!
Harry

  • name: KrbPasswordExpiration date/time demo
    hosts: localhost
    vars:
    user_show:
    json:
    result:
    result:
    krbpasswordexpiration: [
    {
    datetime”: “20220207191401Z”
    }
    ]
    tasks:

  • name: Read and manipulate the krb password expiration date/time.
    set_fact:
    utc: ‘{{ lookup(’‘pipe’‘,
    ‘‘date -u --date="’’ +
    (user_show.json.result.result.krbpasswordexpiration[0][’‘datetime’‘]|
    regex_replace(’‘^(\d\d\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)Z’‘,’‘\1-\2-\3 \4:\5:\6 UTC’‘)) +
    ‘’" “+%Y-%m-%d %H:%M:%S %z”’‘) }}’

  • name: Do you have the time?
    debug:
    msg:

  • “Before: {{ user_show.json.result.result.krbpasswordexpiration[0][‘datetime’] }}”

  • “After: {{ utc }}”

> What I’m ultimately trying to do is grab a user’s expiration date, and compare it to

> the current date. If the date is greater than or equal to 180, I need to disable the

> account.

That’s a different problem. Behold:

  • name: Save “now - 180 days” in krbpasswordexpiration format
    set_fact:
    krbpassworddeadline: ‘{{ lookup(’‘pipe’‘,
    ‘‘date -u --date=“now - 180 days” +%Y%m%d000000Z’’) }}’

  • name: Show the deadline
    debug:
    msg: “krbpassworddeadline: {{ krbpassworddeadline }}”

  • name: Expire user
    debug:
    msg:

  • “This is where you’d expire the user IFF the following were True:”

  • “when: krbpassworddeadline > user_show.json.result.result.krbpasswordexpiration[0][‘datetime’]”

  • “i.e. when: {{ krbpassworddeadline }} > {{ user_show.json.result.result.krbpasswordexpiration[0][‘datetime’] }}”

This produces the following output:

TASK [Ansible setup date-time in UTC] ********************************************************************
task path: /home/utoddl/ansible/date-utc2.yml:29
ok: [localhost] => changed=false
ansible_facts:
utc: 2022-02-07 19:14:01 +0000

TASK [Do you have the time?] *****************************************************************************
task path: /home/utoddl/ansible/date-utc2.yml:40
ok: [localhost] =>
msg:

  • ‘Before: 20220207191401Z’
  • ‘After: 2022-02-07 19:14:01 +0000’

TASK [Save “now - 180 days” in krbpasswordexpiration format] *********************************************
task path: /home/utoddl/ansible/date-utc2.yml:51
ok: [localhost] => changed=false
ansible_facts:
krbpassworddeadline: 20210521000000Z

TASK [Show the deadline] *********************************************************************************
task path: /home/utoddl/ansible/date-utc2.yml:55
ok: [localhost] =>
msg: ‘krbpassworddeadline: 20210521000000Z’

TASK [Expire user] ***************************************************************************************
task path: /home/utoddl/ansible/date-utc2.yml:59
ok: [localhost] =>
msg:

  • ‘This is where you’‘d expire the user IFF the following were True:’
  • ‘when: krbpassworddeadline > user_show.json.result.result.krbpasswordexpiration[0][’‘datetime’‘]’
  • ‘i.e. when: 20210521000000Z > 20220207191401Z’
    META: ran handlers
    META: ran handlers

PLAY RECAP ***********************************************************************************************
localhost : ok=6 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

Thanks for that! I can now reference the datetime member, but now I’m trying to convert that to a datetime object in the format of YYmmdd, but I get an error. Here’s how I’m setting the fact now:

  • name: Set facts
    set_fact:
    user_expires: “{{ user_show.json.result.result.krbpasswordexpiration[0][‘datetime’] | to_datetime(‘%Y%m%d’) }}”

  • name: print results
    debug:
    msg: “{{ user_expires }} / {{ ansible_date_time.year }}{{ ansible_date_time.month }}{{ ansible_date_time.day }}”

When I run the playbook, it gives me the following error in the set_fact play:

FAILED! => {“msg”: "the field ‘args’ has an invalid value ({u’user_expires’: u"{{ user_show.json.result.result.krbpasswordexpiration[0][‘datetime’] | to_datetime(‘%Y%m%d’) }}"}), and could not be converted to an dict.The error was: unconverted data remains: 191401Z\n\n

Any ideas?

Thanks,
Harry

You need to specify the exact matching format.

Looking at the string at hand (‘20220207191401Z’), try something like:

to_datetime(‘%Y%m%d%H%M%SZ’)

Just some practical tip, if you find yourself needing to isolate things like some jinja/ansible filters, it can be quickly done using ad hoc:

dick.visser@GA0267 ~$ ansible -m debug -i localhost, all -a “msg={{ ‘20220207191401Z’ | to_datetime(‘%Y%m%d’) }}”
localhost | FAILED! => {
“msg”: “the field ‘args’ has an invalid value ({‘msg’: "{{ ‘20220207191401Z’ | to_datetime(‘%Y%m%d’) }}"}), and could not be converted to an dict.The error was: unconverted data remains: 191401Z”
}

Adjust the pattern:

dick.visser@GA0267 ~$ ansible -m debug -i localhost, all -a “msg={{‘20220207191401Z’|to_datetime(‘%Y%m%d%H%M%SZ’) }}”
localhost | SUCCESS => {
“msg”: “2022-02-07 19:14:01”
}

You don’t need a datetime object. You just need to know if the string you have sorts before or after this string:

‘{{ lookup(’‘pipe’‘, ‘‘date -u --date=“now - 180 days” +%Y%m%d000000Z’’) }}’
I would argue “now - 180 days” is going to be a lot easier to understand next year than whatever “pure Ansible” contortions you come up with to do the same thing.

Let me further clarify what I’m looking to do and show where I’m at. We have some bash scripts that check our IPA server user’s and determines their password age. Any user who has not changed their password in 180 days gets automatically disabled. So that expiration date will be in past. In bash we found an “abs” function to make a negative date calculation a positive value. For example, if a person’s account has been expired since May 19, 2021, that is 182 days ago and should be expired. I’m trying to do something similar with Ansible. But I’m working on the date calculations now before expanding.

So I’m able to get the user’s password expiration date using the IPA APIs, and can set the fact for it as assisted above in this thread. I can also get today’s date using the Ansible facts. What I am having an issue with now is trying to use the facts previously calculated to determine the age, and its not working:

  • name: Set facts
    set_fact:
    utc: ‘{{ lookup(’‘pipe’‘,
    ‘‘date -u --date="’’ +
    (user_show.json.result.result.krbpasswordexpiration[0][’‘datetime’‘]|
    regex_replace(’‘^(\d\d\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)Z’‘,’‘\1-\2-\3 \4:\5:\6 UTC’‘)) + ‘’" “+%Y-%m-%d”’‘) }}’
    today: “{{ (ansible_date_time.date | to_datetime(‘%Y-%m-%d’)).date() }}”

age: “{{ ((utc) - (today)).days }}”

FAILED! => {“msg”: “Unexpected templating type error occurred on ({{ ((utc) - (today)).days }}): unsupported operand type(s) for -: ‘AnsibleUnsafeText’ and ‘AnsibleUnsafeText’”}

Any ideas on how to reference the “utc” and “today” facts in the “age” fact?

Thanks,
Harry

Both utc and today are strings (a.k.a. “AnsibleUnsafeText”), so you can’t subtract one from the other. If they were both Python datetime objects, then you could get a difference like that (or something similar).

I’m evidently not communicating clearly, but please know that I have a lot of sympathy for the frustration you’re encountering with data manipulation in Ansible. Been there; got some scorched t-shirts out of it. I see what you’re trying to do, and, while you’re not wrong, and you can make Ansible do it that way, it involves a lot more “code” than is necessary to solve your problem.

I say that because you don’t need “today” or “age”. Nor do you need to manipulate any of the kerberos password “datetime” values, or convert anything to datetime objects or take their differences. The only new piece of data you need is a string representing the date that was 180 days ago.

Here’s another shot at demonstrating what I’m suggesting.

If you really want to do it with math (which requires lots of casts to integers) instead of strings, here’s a version that does it that way.

Thanks for that info! It works pretty much how I need it. Now I need to figure out how to loop through all users in our IPA server, get the password expiration, do the calculations, and then disable where needed. The IPA user_find API method doesn’t return the krbpasswordexpiration. So I may need to do the user find and pull out the user names into a fact, then traverse that using the user_show IPA API method to get the krbpasswordexpiration value, then go from there. Still planning that part out, but this calculation method is a huge step, and I appreciate everyone’s time and effort to help.

Thanks,
Harry