E-Mail All Users 10 Day before Password Expiration

This topic contains 4 replies, has 3 voices, and was last updated by Profile photo of CHCH-DOT CHCH-DOT 3 years, 5 months ago.

  • Author
    Posts
  • #12045
    Profile photo of CHCH-DOT
    CHCH-DOT
    Participant

    There is a great PowerShell Script out there to email folks 10 days prior to password expiration.....

    Set-ExecutionPolicy UnRestricted
    cls
    #############################################################################
    # Created by Levente Veres (bergermanus)
    # Contact: http://my.bergersoft.net
    # Description: The current script send Alert for users before they password
    # expires. You can set some values to configure this script.
    ############################################################################

    ###############################################################################
    # Get The max Password age from AD
    ###############################################################################

    function Get-maxPwdAge{
    $root = [ADSI]"LDAP://chch.loc"
    $filter = "(&(objectcategory=domainDNS)(distinguishedName=DC=codespring,DC=local))"
    $ds = New-Object system.DirectoryServices.DirectorySearcher($root,$filter)
    $dc = $ds.findone()

    $dc.Properties | fl
    [int64]$mpa = ($dc.Properties['maxpwdage'][0]).ToString().Trim("-")

    #$mpa*(.000000100)/86400
    }

    ###############################################################################
    # Function to send email to each user
    ###############################################################################
    function send_email_user ($remaining_day, $email, $name )
    {
    $today = Get-Date
    $date_expire = [DateTime]::Now.AddDays($remaining_day) ;
    $SmtpClient = new-object system.net.mail.smtpClient
    $mailmessage = New-Object system.net.mail.mailmessage
    $SmtpClient.Host = "astaro.chch.loc"
    $mailmessage.from = "helpdesk@chch.org"
    $mailmessage.To.add($email)
    $mailmessage.Bcc.add("teach@chch.org")
    $mailmessage.Subject = “$name, Your Domain & E-Mail Password Expires ”
    $mailmessage.IsBodyHtml = $true
    $mailmessage.Body = "

    Dear $name

    "
    $mailmessage.Body +="

    Your login password for account $email on use of the CH-CH computers, Wi-Fi and Email will be expire in $remaining_day days on $date_expire

    "
    $mailmessage.Body +="Please login to a CH-CH lab computer or a Faculty/Staff Laptop connected with a Network Cable. You then can change your password. Changing your password can be done by hitting CTRL-ALT-DEL and selecting change password. Failing to do so could result in getting locked out of the system then requiring a password reset by the help desk!

    "
    $mailmessage.Body += " Generated on : $today

    "
    $mailmessage.Body += "==================================
    "
    $mailmessage.Body += "CH-CH Technology Help Desk
    "
    $mailmessage.Body += "HelpDesk@chch.org
    "
    $smtpclient.Send($mailmessage)
    }

    ###############################################################################
    # Send REPORT for Admins
    ###############################################################################
    function sendmail($body)
    {
    $today = Get-Date
    $SmtpClient = new-object system.net.mail.smtpClient
    $mailmessage = New-Object system.net.mail.mailmessage
    $SmtpClient.Host = "astaro.chch.loc"
    $mailmessage.from = "helpdesk@chch.org"
    $mailmessage.To.add("helpdesk@chch.org")
    $mailmessage.Subject = “[Report] chch.loc password expires”
    $mailmessage.IsBodyHtml = $true
    $mailmessage.Body = "

    Generated on : $today `n

    " + $body
    $mailmessage.Body += "`n" + $body1

    $smtpclient.Send($mailmessage)
    }

    ###############################################################################
    # Search for the active directory users with following conditions
    # 1. Is in USER category
    # 2. Is loged in more that 1 times – for eliminate the system accounts
    # 3. Eliminate the Disbaled Accounts
    ###############################################################################
    $strFilter = "(&(objectCategory=User)(logonCount>=1)(!userAccountControl:1.2.840.113556.1.4.803:=2))"
    $objDomain = New-Object System.DirectoryServices.DirectoryEntry
    $objSearcher = New-Object System.DirectoryServices.DirectorySearcher
    $objSearcher.SearchRoot = $objDomain
    $objSearcher.PageSize = 1000
    $objSearcher.Filter = $strFilter
    $colResults = $objSearcher.FindAll();

    #SET the max day before expiration alert
    $max_alert = 10

    ###############################################################################
    #SET the max password lifetime
    # In the future i rewrite to ask teh GP for the group.
    ###############################################################################

    $max_pwd_life= get-maxPwdAge;

    $userlist = @()

    foreach ($objResult in $colResults)

    {$objItem = $objResult.Properties;
    if ( $objItem.mail.gettype.IsInstance -eq $True)
    {
    $user_name = $objItem.name
    $user_email = $objItem.email
    #Transform the DateTime readable
    $user_logon = [datetime]::FromFileTime($objItem.lastlogon[0])
    $result = $objItem.pwdlastset
    $user_pwd_last_set = [datetime]::FromFileTime($result[0])

    #calculate the difference in Day
    $diff_date = [INT]([DateTime]::Now – $user_pwd_last_set).TotalDays;

    if (($max_pwd_life – $diff_date) -le $max_alert) {
    $selected_user = New-Object psobject
    $selected_user | Add-Member NoteProperty -Name "Name" -Value $objItem.name[0]
    $selected_user | Add-Member NoteProperty -Name "Email" -Value $objItem.mail[0]
    $selected_user | Add-Member NoteProperty -Name "LastLogon" -Value $user_logon
    $selected_user | Add-Member NoteProperty -Name "LastPwdSet" -Value $user_pwd_last_set
    $selected_user | Add-Member NoteProperty -Name "EllapsedDay" -Value $diff_date
    $selected_user | Add-Member NoteProperty -Name "RemainingDay" -Value ($max_pwd_life-$diff_date)
    $userlist+=$selected_user

    }
    }
    }

    ###############################################################################
    # Send email for each user
    ###############################################################################
    foreach ($userItem in $userlist )
    {
    send_email_user $userItem.RemainingDay $userItem.Email $userItem.Name
    }

    ###############################################################################
    # Sedn email for Admins in reporting format
    ###############################################################################
    $bodyme = $userlist| Sort-Object "RemainingDay" | ConvertTo-Html -Title "AD password Status" -Body "

    Ad password expiration Status

    " -head "

    " | foreach {$_ -replace "

    ", "
    "}

    sendmail $bodyme

    ###############################################################################
    # END
    ###############################################################################

    However, I ran into a couple of small problems:

    1. Everyone in the Organization got an email rather than just a few people, who's passwords will expire soon.
    2. Remaining Days for Password expiration is a negative number. ie: your password will expire in -200 days.
    Your login password for account DOT@chch.org on use of the CH-CH computers, Wi-Fi and Email will be expire in -682 days on 01/31/2012 09:45:35
    3. Program listed an exception when it ran:
    Cannot index into a null array.
    At passwordexpiration.ps1:21 char:1
    [int64]$mpa = ($dc.Properties['maxpwdage'][0]).ToString().Trim("-")
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : NullArray

    However, I am not sure how to fix this problem. I emailed Levente, but have not heard back. Any ideas?

  • #12047
    Profile photo of Dave Wyatt
    Dave Wyatt
    Moderator

    I haven't stepped through all of the code (so there may be other issues), but your original problem is in this function. It's a mess:

    function Get-maxPwdAge{
        $root = [ADSI]“LDAP://chch.loc”
        $filter = “(&(objectcategory=domainDNS)(distinguishedName=DC=codespring,DC=local))”
        $ds = New-Object system.DirectoryServices.DirectorySearcher($root,$filter)
        $dc = $ds.findone()
    
        $dc.Properties | fl
        [int64]$mpa = ($dc.Properties['maxpwdage'][0]).ToString().Trim(“-”)
    
        #$mpa*(.000000100)/86400
    }
    
    • The domain name (both in DNS and Distinguished Name form) are hard-coded. This may be acceptable to you, but make sure the values are correct. It's a little but suspicious that the DNS and Distinguished Names look so different ("chch.loc" versus "DC=codespring,DC=local").
    • There's no error checking code whatsoever. $ds.FindOne() can return null, if no matching object is found (and if the hard-coded values mentioned above are wrong, that's exactly what will happen.) That would lead to the "cannot index into null array" error message you're seeing.
    • As written, the line that was supposed to output the max password age (as a number of days) is commented out, and instead you're outputting the results of $dc.Properties | fl. I have no idea what impact that would have on the rest of the code.

    If your environment supports the minimum requirements for the ActiveDirectory module, you'll find it's much easier to use that, rather than this code (which was probably written before PowerShell 2.0 was released.) For example, to determine the password age in days, it's as simple as this:

    (Get-ADDefaultDomainPasswordPolicy).MaxPasswordAge.Days
    

    To use the ActiveDirectory module, you require the following:

    • At least one Domain Controller running Windows Server 2008 R2 or later with AD Web Service running (which is installed by default when you promote a DC), OR at least one Domain Controller running Windows Server 2003 SP2 or Windows Server 2008 with the AD Management Gateway Service installed (available as a free download from Microsoft.)
    • The script must be running on a machine with the AD Module installed, which is part of the Remote Server Administration Tools package. This can be added as a Windows Feature on Server 2008 R2 or later, and is available as a download for clients running Windows 7 or later.
    • #12099
      Profile photo of CHCH-DOT
      CHCH-DOT
      Participant

      Thanks Dave! Perfect... I modified the code and it works great....

  • #12048
    Profile photo of Joakim Svendsen
    Joakim Svendsen
    Participant

    I didn't look at the code much. Looks messy, though, as Dave is pointing out.

    Sadly what I'm about to post is an ancient script (one of my very first PowerShell scripts), so it's not very standards-compliant in some ways (private variables, for one!), but I've used a variant of this script in an organization for warning via email for years, so I know it works. It logs errors and successfully sent mail to a file. It requires Quest ActiveRoles.

    Might want to check it out: http://www.powershelladmin.com/wiki/Active_directory_password_expiration_notification

  • #12350
    Profile photo of CHCH-DOT
    CHCH-DOT
    Participant

    Set-ExecutionPolicy UnRestricted
    import-module activedirectory
    cls
    #############################################################################
    # Created by Levente Veres (bergermanus)
    # Contact: http://my.bergersoft.net
    # Modified by Jim Haynie, Director of Technology at chch.org
    # Modifications include: Replaced Maximum Password Age Function, Global Variables at the Top, Variable to exclude expired passwords
    # Description: The current script send Alert for users before they password
    # expires. You can set some values to configure this script.
    ############################################################################

    ###############################################################################
    # Global Variables – Added by Jim Haynie
    ###############################################################################
    #SMTP Outbound Server to send email through
    $mailserver = "yourmailserver"

    #Help Desk E-Mail or admin
    $adminemail = "tech@yourdomain"

    #Company or School Name
    $organization = "Org Name"

    #Signature for email
    $signed = "$organization Technology Help Desk"

    #Microsoft local domain name
    $domain= "yourdomain.loc"

    #SET the max day before expiration alert
    $max_alert = 10

    #SET the amount of days after expiration alert
    $post_alert = 0

    ###############################################################################
    # Get The max Password age from AD – Replaced by Jim Haynie
    ###############################################################################
    function Get-maxPwdAge
    {
    (Get-ADDefaultDomainPasswordPolicy).MaxPasswordAge.Days
    }

    ###############################################################################
    # Function to send email to each user
    ###############################################################################
    function send_email_user ($remaining_day, $email, $name )
    {
    $today = Get-Date
    $date_expire = [DateTime]::Now.AddDays($remaining_day) ;
    $SmtpClient = new-object system.net.mail.smtpClient
    $mailmessage = New-Object system.net.mail.mailmessage
    $SmtpClient.Host = $mailserver
    $mailmessage.from = $adminemail
    $mailmessage.To.add($email)
    $mailmessage.Bcc.add($adminemail)
    $mailmessage.Subject = “$name, Your Domain & E-Mail Password Expires ”
    $mailmessage.IsBodyHtml = $true
    $mailmessage.Body = "

    Dear $name

    "
    $mailmessage.Body +="

    Your login password for account $email on use of the $organization computers, Wi-Fi and Email will be expire in $remaining_day days on $date_expire

    "
    $mailmessage.Body +="Please login to a $organization lab computer or a Laptop connected with a Network Cable. You then can change your password. Changing your password can be done by hitting CTRL-ALT-DEL and selecting change password. Failing to do so could result in getting locked out of the system then requiring a password reset by the help desk!

    "
    $mailmessage.Body += " Generated on : $today

    "
    $mailmessage.Body += "==================================
    "
    $mailmessage.Body += "$signed
    "
    $mailmessage.Body += "$adminemail
    "
    $smtpclient.Send($mailmessage)
    }

    ###############################################################################
    # Send REPORT for Admins
    ###############################################################################
    function sendmail($body)
    {
    $today = Get-Date
    $SmtpClient = new-object system.net.mail.smtpClient
    $mailmessage = New-Object system.net.mail.mailmessage
    $SmtpClient.Host = $mailserver
    $mailmessage.from = $adminemail
    $mailmessage.To.add($adminemail)
    $mailmessage.Subject = “[Report] $domain password expires”
    $mailmessage.IsBodyHtml = $true
    $mailmessage.Body = "

    Generated on : $today `n

    " + $body
    $mailmessage.Body += "`n" + $body1

    $smtpclient.Send($mailmessage)
    }

    ###############################################################################
    # Search for the active directory users with following conditions
    # 1. Is in USER category
    # 2. Is loged in more that 1 times – for eliminate the system accounts
    # 3. Eliminate the Disbaled Accounts
    # 4. Select Passwords based upon a range from postalert to maxalert – Added by Jim Haynie
    # (This eliminates expired accounts or accounts that do not change password)
    ###############################################################################
    $strFilter = "(&(objectCategory=User)(logonCount>=1)(!userAccountControl:1.2.840.113556.1.4.803:=2))"
    $objDomain = New-Object System.DirectoryServices.DirectoryEntry
    $objSearcher = New-Object System.DirectoryServices.DirectorySearcher
    $objSearcher.SearchRoot = $objDomain
    $objSearcher.PageSize = 1000
    $objSearcher.Filter = $strFilter
    $colResults = $objSearcher.FindAll();

    ###############################################################################
    #SET the max password lifetime
    ###############################################################################
    $max_pwd_life= get-maxPwdAge;

    $userlist = @()

    foreach ($objResult in $colResults)

    {$objItem = $objResult.Properties;
    if ( $objItem.mail.gettype.IsInstance -eq $True)
    {
    $user_name = $objItem.name
    $user_email = $objItem.email
    #Transform the DateTime readable
    $user_logon = [datetime]::FromFileTime($objItem.lastlogon[0])
    $result = $objItem.pwdlastset
    $user_pwd_last_set = [datetime]::FromFileTime($result[0])

    #calculate the difference in Day
    $diff_date = [INT]([DateTime]::Now – $user_pwd_last_set).TotalDays;

    #calculate the remaining days – Added by Jim Haynie
    $remaining_days = ($max_pwd_life – $diff_date)

    if ($remaining_days -le $max_alert -and $remaining_days -ge $post_alert)
    {
    $selected_user = New-Object psobject
    $selected_user | Add-Member NoteProperty -Name "Name" -Value $objItem.name[0]
    $selected_user | Add-Member NoteProperty -Name "Email" -Value $objItem.mail[0]
    $selected_user | Add-Member NoteProperty -Name "Last Logon" -Value $user_logon
    $selected_user | Add-Member NoteProperty -Name "Last Pwd Set" -Value $user_pwd_last_set
    $selected_user | Add-Member NoteProperty -Name "Ellapsed Days" -Value $diff_date
    $selected_user | Add-Member NoteProperty -Name "Remaining Days" -Value $remaining_days
    $userlist+=$selected_user

    }
    }
    }

    ###############################################################################
    # Send email for each user
    ###############################################################################
    foreach ($userItem in $userlist )
    {
    send_email_user $userItem.RemainingDay $userItem.Email $userItem.Name
    }

    ###############################################################################
    # Sedn email for Admins in reporting format
    ###############################################################################
    $bodyme = $userlist| Sort-Object "RemainingDay" | ConvertTo-Html -Title "AD password Status" -Body "

    Ad password expiration Status

    " -head "

    " | foreach {$_ -replace "

    ", "
    "}

    sendmail $bodyme

    ###############################################################################
    # END
    ###############################################################################

You must be logged in to reply to this topic.