Creating an AD password expiration date Extension Attribute

njwinter
New Contributor II

This Extension Attribute is designed to return the number of days remaining until Active Directory password expiration. Run as a standalone script it works fine, it returns the integer "212" for my account. However, if I create a Smart Group in the JSS based on this EA it doesn't populate with any data. The field is blank for all users when viewed in inventory list, even after recon. Am I missing something obvious?

Smart Group parameters:
"AD Password Expiration" is less than 30

Extension Attribute:
Name: AD Password Expiration
Data type: integer
Inventory Display: Extension Attributes
Input type: Script

#!/bin/bash

# Logged in user
LoggedInUser=`ls -l /dev/console | awk '{ print $3 }'`

# Current password change policy
PasswdPolicy=365

# Last password set date
LastPasswordSet=`dscl /Active Directory/CORP/All Domains/ read /Users//$LoggedInUser SMBPasswordLastSet | awk '{print $2}'`

# Calculations
LastPasswordCalc1=`expr $LastPasswordSet / 10000000 - 1644473600`
LastPasswordCalc2=`expr $LastPasswordCalc1 - 10000000000`
TimeStampToday=`date +%s`
TimeSinceChange=`expr $TimeStampToday - $LastPasswordCalc2`
DaysSinceChange=`expr $TimeSinceChange / 86400`
DaysRemaining=`expr $PasswdPolicy - $DaysSinceChange`

echo $DaysRemaining

exit 0

JSS 9.3, Mac OS X

8 REPLIES 8

bentoms
Release Candidate Programs Tester

Hey @njwinter,

Does it work when run as root on your mac?

Secondly, have you looked @ ADPassMon? Here is my fork: http://macmule.com/2014/04/01/announcing-adpassmon-v2-fork/ (that also links to the original).

That displays expiration days to the user & writes the days to a plist that you could report on via an EA.

njwinter
New Contributor II

Yes, the script runs fine as a standalone on my local machine, as mentioned above.
Unfortunately, ADPassMon is not right for our environment.

jhbush
Valued Contributor II

@njwinter you need the last line to be like this ```
echo "<result>$DaysRemaining</result>"
```

bentoms
Release Candidate Programs Tester

@jhbush1973, doh! That's what happens when your replying when on hols. The simple things get missed.

@njwinter, please add the line as per what @jhbush1973 mentioned.

If that resolves, please mark an the answer.

njwinter
New Contributor II

I figured out the <result> tag requirement just as the database corrupted so I haven't been able to test it yet. Thanks!

azbikowski
New Contributor II

Not exactly what you're looking for, but I created a PowerShell script that runs nightly and dumps this info out of Active Directory directly.

It's not (currently) the cleanest code around, but basically it's pulling every user that has an email address ending in @example.com, checking the password age, figuring out how many days until the password expires based on your Active Directory policy, sends an email reminder to the user if their password is expiring in 14, 7, 3, 1, or 0 days, and sending a report to helpdesk@example.com with a list of users who's got an email notification.

I have this setup as a scheduled task on one of our Windows servers so users and the helpdesk get their alert right away in the morning.

I've also rolled out ADPassMon on the Macs.

Even with the email alert and ADPassMon alerts, users still ignore the alerts and find themselves unable to connect to the VPN when their password expires. Mildly infuriating, but that's par for the course for IT Support.

Import-Module ActiveDirectory

$maxdays=(Get-ADDefaultDomainPasswordPolicy).MaxPasswordAge.TotalDays
$summarybody="Name `t ExpireDate `t DaysToExpire `n"

(Get-ADUser -filter {(mail -like "*@example.com") -and (Enabled -eq "True") -and (PasswordNeverExpires -eq "False")} -properties *) | Sort-Object pwdLastSet |
foreach-object {

    $lastset=Get-Date([System.DateTime]::FromFileTimeUtc($_.pwdLastSet))
    $expires=$lastset.AddDays($maxdays).ToShortDateString()
    $daystoexpire=[math]::round((New-TimeSpan -Start $(Get-Date) -End $expires).TotalDays)
    $samname=$_.samaccountname
    $firstname=$_.GivenName
    if (($daystoexpire -eq 14) -or ($daystoexpire -eq 7) -or ($daystoexpire -eq 3) -or ($daystoexpire -eq 1) -or ($daystoexpire -eq 0)) {
    #if ($daystoexpire -le 14) {
        $ThereAreExpiring=$true

        $emailFrom = "helpdesk@example.com"
        $emailTo = "$samname@example.com"
        #$emailTo = "example@example.com"
        if ($daystoexpire -eq 0) {
            $subject = "$firstname, your password has expired!"
            $body = "$firstname,
Your password has expired and you must change it immediately. No further email notifications will be sent. 

Contact support at extension 9119 for assistance.

$samname@example.com"
        }
        Else {
            $subject = "$firstname, your password expires in $daystoexpire day(s)!"
            $body = "$firstname,
Your password expires in $daystoexpire day(s).

If you are using a Windows computer, press Ctrl + Alt + Del the click Change password.

If you are using a Mac computer follow the instructions at http://example.com to change your password. 

    $samname@example.com"
        }

        $smtpServer = "email.example.com"
        $smtp = new-object Net.Mail.SmtpClient($smtpServer)
        $smtp.Send($emailFrom, $emailTo, $subject, $body)   

        $summarybody += "$samname `t $expires `t $daystoexpire `n"
    }
    elseif ($daystoexpire -lt 0) {
        $ThereAreExpiring=$true
        # Add a note to the report email, but don't notify user. 
        $summarybody += "$samname `t $expires `t $daystoexpire `n"
    }
}
if ($ThereAreExpiring) {
    $emailFrom = "helpdesk@redbrickheatlh.com"
    $emailTo = "helpdesk@example.com"
    #$emailTo = "example@example.com"
    $subject = "Expiring passwords"
    $body = $summarybody
    $smtpServer = "email.example.com"
    $smtp = new-object Net.Mail.SmtpClient($smtpServer)
    $smtp.Send($emailFrom, $emailTo, $subject, $body)
}

emily
Valued Contributor III
Valued Contributor III

When I use the script from @njwinter the return is 5 days off for some reason. Maybe I need to fix some of the math to account for a 90 day expiry rather than 365?

signetmac
Contributor

Sorry I didn't read this article before I posted a new thread. I added a script here for accomplishing the same thing, only using LDAP, which most people are already using in order to assign assets to end users under the Users & Location pane in JSS. This way we can create an extension attribute even if the organization chooses not to bind computers to AD. Here it is so you don't have to jump threads:

#!/bin/bash

################################################
# Created By: Mac Scott, macscott[at]livenation.com
# Creation Date: November 4, 2015
# Last modified: November 4, 2015 by macscott[at]livenation.com
# Brief Description: Scrapes JSS for username assigned to asset, then does AD LDAP lookup to determine AD 
################################################
# Set up your organization's variables
# For JSS:

jss_server=     # Full URL for logging into your JSS instance, quoted b/c of the characters; i.e. 'https://jss.yourorg.com:8443'
rest_account=   # An account on your jss server with limited privs set up to scrape user info from the site
rest_password=  # Password for the rest_account user above

# For AD/LDAP:
ldap_server=    # IP address of a dc within your org that allows directory lookups
ldap_account=   # AD account with limited privs, used to query the active directory server using ldapsearch
ldap_password=  # Password for the ldap_account user above
forest_domain=  # i.e. forest.yourorg.com
dist_name=  # The distinguished name of your domain and forest in AD, quoted b/c of the characters; i.e. 'dc=forest,dc=yourorg,dc=com'
pwdPolicy=  # Days to password expiration since last set i.e.: 90

################################################
# Program logic:
# Determine if on corporate network

if ping -c 1 $ldap_server &>/dev/null ; then

    # Determine Primary MAC address of computer and pull API data
    en0=`ifconfig en0 | grep -o -E '([[:xdigit:]]{1,2}:){5}[[:xdigit:]]{1,2}'`
    jssxml="`curl --silent --insecure -u $rest_account:$rest_password $jss_server/JSSResource/computers/macaddress/$en0/subset/Location -X GET`"

    # Get name of user the computer was assigned to
    trimName=`echo ${jssxml#*"<username>"}`
    usernameINjss=`echo ${trimName%"</username>"*}`

    # LDAP lookup for pwdLastSet and then a little math to arrive at days until expiry. 
    # Credit for this portion of the script goes to Jason Borchardt, aka colonelpanic on jamfNation

    lastpwdMS=`ldapsearch -x -H ldap://$ldap_server -D $ldap_account@$forest_domain -w "$ldap_password" -b "$dist_name" -L "anr=$usernameINjss" pwdLastSet | /usr/bin/awk '/pwdLastSet:/{print $2}'`
    lastpwdUNIX1=`expr $lastpwdMS / 10000000 - 1644473600`
    lastpwdUNIX=`expr $lastpwdUNIX1 - 10000000000`
    todayUNIX=`date +%s`
    diffDays1=`expr $todayUNIX - $lastpwdUNIX`
    diffDays=`expr $diffDays1 / 86400`
    daysRemaining=`expr $pwdPolicy - $diffDays`
    echo "<result>$daysRemaining</result>"
fi