Skip to content

Instantly share code, notes, and snippets.

@geegeek
Forked from cloudcap10/0-Readme.md
Created November 3, 2022 09:51
Show Gist options
  • Save geegeek/8d2926145e8eeae898bd1cde9cbdb043 to your computer and use it in GitHub Desktop.
Save geegeek/8d2926145e8eeae898bd1cde9cbdb043 to your computer and use it in GitHub Desktop.

Revisions

  1. @cloudcap10 cloudcap10 revised this gist May 30, 2022. 3 changed files with 0 additions and 0 deletions.
    File renamed without changes.
  2. @cloudcap10 cloudcap10 revised this gist May 30, 2022. 2 changed files with 0 additions and 0 deletions.
  3. @cloudcap10 cloudcap10 revised this gist May 30, 2022. No changes.
  4. @cloudcap10 cloudcap10 revised this gist May 18, 2022. 1 changed file with 1 addition and 0 deletions.
    1 change: 1 addition & 0 deletions Readme.md
    Original file line number Diff line number Diff line change
    @@ -4,6 +4,7 @@ This version is a highly modified fork of the original **v1.4** by Robert Pearma

    New in this version:
    - Added Office 365 SMTP - Password-Expiration-Notifications-office365.ps1
    - No SMTP Authentication - Password-Expiration-Notifications.ps1
    - A SearchBase is required.
    - When logging, the CSV will always be overwritten.
    - Accounts with recently-expired passwords can be notified by specifying a "negativedays" value.
  5. @cloudcap10 cloudcap10 revised this gist May 18, 2022. 1 changed file with 1 addition and 0 deletions.
    1 change: 1 addition & 0 deletions Readme.md
    Original file line number Diff line number Diff line change
    @@ -3,6 +3,7 @@ Password-Expiration-Notifications.ps1 is a powerShell script designed to be run
    This version is a highly modified fork of the original **v1.4** by Robert Pearman from https://gallery.technet.microsoft.com/Password-Expiry-Email-177c3e27. Pearman's 2.x version was completely re-written.

    New in this version:
    - Added Office 365 SMTP - Password-Expiration-Notifications-office365.ps1
    - A SearchBase is required.
    - When logging, the CSV will always be overwritten.
    - Accounts with recently-expired passwords can be notified by specifying a "negativedays" value.
  6. @cloudcap10 cloudcap10 revised this gist May 18, 2022. 1 changed file with 242 additions and 0 deletions.
    242 changes: 242 additions & 0 deletions Password-Expiration-Notifications-office365.ps1
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,242 @@
    #################################################################################################################
    #
    # Modified by: talzcloning
    #
    # Originally from v1.4 @ https://gallery.technet.microsoft.com/Password-Expiry-Email-177c3e27
    # Robert Pearman (WSSMB MVP)
    # TitleRequired.com
    # Script to Automated Email Reminders when Users Passwords due to Expire.
    #
    # Requires: Windows PowerShell Module for Active Directory
    #
    ##################################################################################################################
    # Please Configure the following variables....
    $SearchBase="DC=domain,DC=com"
    $smtpServer="smtp.office365.com"
    $SMTPPort = "587"
    $SMTPUsername = "[email protected]"
    $GetPassword = Get-Content "C:\temp\password.txt" #File with password has restricted access
    $SMTPPassword = $GetPassword | ConvertTo-SecureString -AsPlainText -Force
    $SMTPCredentials = new-object Management.Automation.PSCredential ($SMTPUsername,$SMTPPassword)
    $expireindays = 10 #number of days of soon-to-expire paswords. i.e. notify for expiring in X days (and every day until $negativedays)
    $negativedays = -1 #negative number of days (days already-expired). i.e. notify for expired X days ago
    $from = "Password Expiry <[email protected]>"
    $logging = $true # Set to $false to Disable Logging
    $logNonExpiring = $false
    $logFile = "C:\Logs\PS-pwd-expiry.csv" # ie. c:\mylog.csv
    $testing = $true # Set to $false to Email Users
    $adminEmailAddr = "[email protected]" #multiple addr allowed but MUST be independent strings separated by comma
    $sampleEmails = 1 #number of sample email to send to adminEmailAddr when testing ; in the form $sampleEmails="ALL" or $sampleEmails=[0..X] e.g. $sampleEmails=0 or $sampleEmails=3 or $sampleEmails="all" are all valid.
    #
    ###################################################################################################################

    # System Settings
    $textEncoding = [System.Text.Encoding]::UTF8
    $date = Get-Date -format yyyy-MM-dd

    $starttime=Get-Date #need time also; don't use date from above

    Write-Host "Processing `"$SearchBase`" for Password-Expiration-Notifications"

    #set max sampleEmails to send to $adminEmailAddr
    if ( $sampleEmails -isNot [int]) {
    if ( $sampleEmails.ToLower() -eq "all") {
    $sampleEmails=$users.Count
    } #else use the value given
    }

    if (($testing -eq $true) -and ($sampleEmails -ge 0)) {
    Write-Host "Testing only; $sampleEmails email samples will be sent to $adminEmailAddr"
    } elseif (($testing -eq $true) -and ($sampleEmails -eq 0)) {
    Write-Host "Testing only; emails will NOT be sent"
    }

    # Create CSV Log
    if ($logging -eq $true) {
    #Always purge old CSV file
    Out-File $logfile
    Add-Content $logfile "`"Date`",`"SAMAccountName`",`"DisplayName`",`"Created`",`"PasswordSet`",`"DaystoExpire`",`"ExpiresOn`",`"EmailAddress`",`"Notified`""
    }

    # Get Users From AD who are Enabled, Passwords Expire
    Import-Module ActiveDirectory
    $users = get-aduser -SearchBase $SearchBase -Filter {(enabled -eq $true) -and (passwordNeverExpires -eq $false)} -properties sAMAccountName, displayName, PasswordNeverExpires, PasswordExpired, PasswordLastSet, EmailAddress, lastLogon, whenCreated
    $DefaultmaxPasswordAge = (Get-ADDefaultDomainPasswordPolicy).MaxPasswordAge

    $countprocessed=${users}.Count
    $samplesSent=0
    $countsent=0
    $countnotsent=0
    $countfailed=0

    # Process Each User for Password Expiry
    foreach ($user in $users) {
    $dName = $user.displayName
    $sName = $user.sAMAccountName
    $emailaddress = $user.emailaddress
    $whencreated = $user.whencreated
    $passwordSetDate = $user.PasswordLastSet
    $sent = "" # Reset Sent Flag

    $PasswordPol = (Get-AduserResultantPasswordPolicy $user)
    # Check for Fine Grained Password
    if (($PasswordPol) -ne $null) {
    $maxPasswordAge = ($PasswordPol).MaxPasswordAge
    } else {
    # No FGPP set to Domain Default
    $maxPasswordAge = $DefaultmaxPasswordAge
    }

    #If maxPasswordAge=0 then same as passwordNeverExpires, but PasswordCannotExpire bit is not set
    if ($maxPasswordAge -eq 0) {
    Write-Host "$sName MaxPasswordAge = $maxPasswordAge (i.e. PasswordNeverExpires) but bit not set."
    }

    $expiresOn = $passwordsetdate + $maxPasswordAge
    $today = (get-date)

    if ( ($user.passwordexpired -eq $false) -and ($maxPasswordAge -ne 0) ) { #not Expired and not PasswordNeverExpires
    $daystoexpire = (New-TimeSpan -Start $today -End $expiresOn).Days
    } elseif ( ($user.passwordexpired -eq $true) -and ($passwordSetDate -ne $null) -and ($maxPasswordAge -ne 0) ) { #if expired and passwordSetDate exists and not PasswordNeverExpires
    # i.e. already expired
    $daystoexpire = -((New-TimeSpan -Start $expiresOn -End $today).Days)
    } else {
    # i.e. (passwordSetDate = never) OR (maxPasswordAge = 0)
    $daystoexpire="NA"
    #continue #"continue" would skip user, but bypass any non-expiry logging
    }

    #Write-Host "$sName DtE: $daystoexpire MPA: $maxPasswordAge" #debug

    # Set verbiage based on Number of Days to Expiry.
    Switch ($daystoexpire) {
    {$_ -ge $negativedays -and $_ -le "-1"} {$messageDays = "has expired"}
    "0" {$messageDays = "will expire today"}
    "1" {$messageDays = "will expire in 1 day"}
    default {$messageDays = "will expire in " + "$daystoexpire" + " days"}
    }

    # Email Subject Set Here
    $subject="$sName Windows Login password $messageDays"

    # Email Body Set Here, Note You can use HTML, including Images.
    $body="
    <p>Your Active Directory password for your <b>$sName</b> account $messageDays. After expired, you will not be able to login until your password is changed.</p>
    <p>Please visit selfservice.example.com to change your password. Alternatively, on a Windows machine, you may press Ctrl-Alt-Del and select `"Change Password`".</p>
    <p>If you do not know your current password, <a href='https://selfservice.example.com/?action=sendtoken'>click here to email a password reset link</a>.</p>
    <p>Thank you,<br>
    Example.com Administrator<br>
    [email protected]<br>
    www.example.com/support/<br>
    </p>
    "

    # If testing-enabled and send-samples, then set recipient to adminEmailAddr else user's EmailAddress
    if (($testing -eq $true) -and ($samplesSent -lt $sampleEmails)) {
    $recipient = $adminEmailAddr
    } else {
    $recipient = $emailaddress
    }

    #if in trigger range, send email
    if ( ($daystoexpire -ge $negativedays) -and ($daystoexpire -lt $expireindays) -and ($daystoexpire -ne "NA") ) {
    # Send Email Message
    if (($emailaddress) -ne $null) {
    if ( ($testing -eq $false) -or (($testing -eq $true) -and ($samplesSent -lt $sampleEmails)) ) {
    try {
    Send-Mailmessage -smtpServer $smtpServer -from $from -to $recipient -subject $subject -body $body -bodyasHTML -priority High -Encoding $textEncoding -ErrorAction Stop -ErrorVariable err -port $SMTPPort -UseSsl -Credential $SMTPCredentials
    } catch {
    write-host "Error: Could not send email to $recipient via $smtpServer"
    $sent = "Send fail"
    $countfailed++
    } finally {
    if ($err.Count -eq 0) {
    write-host "Sent email for $sName to $recipient"
    $countsent++
    if ($testing -eq $true) {
    $samplesSent++
    $sent = "toAdmin"
    } else { $sent = "Yes" }
    }
    }
    } else {
    Write-Host "Testing mode: skipping email to $recipient"
    $sent = "No"
    $countnotsent++
    }
    } else {
    Write-Host "$dName ($sName) has no email address."
    $sent = "No addr"
    $countnotsent++
    }

    # If Logging is Enabled Log Details
    if ($logging -eq $true) {
    Add-Content $logfile "`"$date`",`"$sName`",`"$dName`",`"$whencreated`",`"$passwordSetDate`",`"$daystoExpire`",`"$expireson`",`"$emailaddress`",`"$sent`""
    }
    } else {
    #if ( ($daystoexpire -eq "NA") -and ($maxPasswordAge -eq 0) ) { Write-Host "$sName PasswordNeverExpires" } elseif ($daystoexpire -eq "NA") { Write-Host "$sName PasswordNeverSet" } #debug
    # Log Non Expiring Password
    if ( ($logging -eq $true) -and ($logNonExpiring -eq $true) ) {
    if ($maxPasswordAge -eq 0 ) {
    $sent = "NeverExp"
    } else {
    $sent = "No"
    }
    Add-Content $logfile "`"$date`",`"$sName`",`"$dName`",`"$whencreated`",`"$passwordSetDate`",`"$daystoExpire`",`"$expireson`",`"$emailaddress`",`"$sent`""
    }
    }

    } # End User Processing

    $endtime=Get-Date
    $totaltime=($endtime-$starttime).TotalSeconds
    $minutes="{0:N0}" -f ($totaltime/60)
    $seconds="{0:N0}" -f ($totaltime%60)

    Write-Host "$countprocessed Users from `"$SearchBase`" Processed in $minutes minutes $seconds seconds."
    Write-Host "Email trigger range from $negativedays (past) to $expireindays (upcoming) days of user's password expiry date."
    Write-Host "$countsent Emails Sent."
    Write-Host "$countnotsent Emails skipped."
    Write-Host "$countfailed Emails failed."

    if ($logging -eq $true) {
    #sort the CSV file
    Rename-Item $logfile "$logfile.old"
    import-csv "$logfile.old" | sort ExpiresOn | export-csv $logfile -NoTypeInformation
    Remove-Item "$logFile.old"
    Write-Host "CSV File created at ${logfile}."

    #email the CSV and stats to admin(s)
    if ($testing -eq $true) {
    $body="<b><i>Testing Mode.</i></b><br>"
    } else {
    $body=""
    }

    $body+="
    CSV Attached for $date<br>
    $countprocessed Users from `"$SearchBase`" Processed in $minutes minutes $seconds seconds.<br>
    Email trigger range from $negativedays (past) to $expireindays (upcoming) days of user's password expiry date.<br>
    $countsent Emails Sent.<br>
    $countnotsent Emails skipped.<br>
    $countfailed Emails failed.
    "

    try {
    Send-Mailmessage -smtpServer $smtpServer -from $from -to $adminEmailAddr -subject "Password Expiry Logs" -body $body -bodyasHTML -Attachments "$logFile" -priority High -Encoding $textEncoding -ErrorAction Stop -ErrorVariable err -port $SMTPPort -UseSsl -Credential $SMTPCredential
    } catch {
    write-host "Error: Failed to email CSV log to $adminEmailAddr via $smtpServer"
    } finally {
    if ($err.Count -eq 0) {
    write-host "CSV emailed to $adminEmailAddr"
    }
    }
    }

    # End
  7. meoso revised this gist Apr 12, 2018. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion Password-Expiration-Notifications.ps1
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,6 @@
    #################################################################################################################
    #
    # Password-Expiration-Notifications v20180329
    # Password-Expiration-Notifications v20180412
    # Highly Modified fork. https://gist.github.com/meoso/3488ef8e9c77d2beccfd921f991faa64
    #
    # Originally from v1.4 @ https://gallery.technet.microsoft.com/Password-Expiry-Email-177c3e27
  8. meoso revised this gist Apr 12, 2018. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion Password-Expiration-Notifications.ps1
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,6 @@
    #################################################################################################################
    #
    # Password-Expiration-Notifications v20170301
    # Password-Expiration-Notifications v20180329
    # Highly Modified fork. https://gist.github.com/meoso/3488ef8e9c77d2beccfd921f991faa64
    #
    # Originally from v1.4 @ https://gallery.technet.microsoft.com/Password-Expiry-Email-177c3e27
  9. meoso revised this gist Apr 12, 2018. No changes.
  10. meoso revised this gist Apr 12, 2018. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion Readme.md
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,4 @@
    Password-Expiration-Notifications.ps1 is a powerShell script designed to be run on a schedule to automatically email Active Directory users of upcoming password expirations and recently expired passwords.
    Password-Expiration-Notifications.ps1 is a powerShell script designed to be run on a schedule to automatically email Active Directory users of soon-to-expire and recently-expired passwords.

    This version is a highly modified fork of the original **v1.4** by Robert Pearman from https://gallery.technet.microsoft.com/Password-Expiry-Email-177c3e27. Pearman's 2.x version was completely re-written.

  11. meoso revised this gist Apr 12, 2018. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion Readme.md
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,6 @@
    Password-Expiration-Notifications.ps1 is a powerShell script designed to be run on a schedule to automatically email Active Directory users of upcoming password expirations and recently expired passwords.

    This version is a highly modified fork of the original **v1.4** by Robert Pearman from https://gallery.technet.microsoft.com/Password-Expiry-Email-177c3e27. RPearman's 2.x version was completely re-written.
    This version is a highly modified fork of the original **v1.4** by Robert Pearman from https://gallery.technet.microsoft.com/Password-Expiry-Email-177c3e27. Pearman's 2.x version was completely re-written.

    New in this version:
    - A SearchBase is required.
  12. meoso revised this gist Apr 12, 2018. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion Readme.md
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,6 @@
    Password-Expiration-Notifications.ps1 is a powerShell script designed to be run on a schedule to automatically email Active Directory users of upcoming password expirations and recently expired passwords.

    This version is a highly modified fork of the original v1.4 by Robert Pearman from https://gallery.technet.microsoft.com/Password-Expiry-Email-177c3e27
    This version is a highly modified fork of the original **v1.4** by Robert Pearman from https://gallery.technet.microsoft.com/Password-Expiry-Email-177c3e27. RPearman's 2.x version was completely re-written.

    New in this version:
    - A SearchBase is required.
  13. meoso revised this gist Apr 12, 2018. 2 changed files with 3 additions and 3 deletions.
    4 changes: 2 additions & 2 deletions Password-Expiration-Notifications.ps1
    Original file line number Diff line number Diff line change
    @@ -14,8 +14,8 @@
    # Please Configure the following variables....
    $SearchBase="DC=EXAMPLE,DC=COM"
    $smtpServer="smtp.example.com"
    $expireindays = 7 #number of days prior notice to email notification
    $negativedays = -3 #negative number of days (days already expired) to email notification
    $expireindays = 7 #number of days of soon-to-expire paswords. i.e. notify for expiring in X days (and every day until $negativedays)
    $negativedays = -3 #negative number of days (days already-expired). i.e. notify for expired X days ago
    $from = "Administrator <[email protected]>"
    $logging = $true # Set to $false to Disable Logging
    $logNonExpiring = $false
    2 changes: 1 addition & 1 deletion Readme.md
    Original file line number Diff line number Diff line change
    @@ -5,7 +5,7 @@ This version is a highly modified fork of the original v1.4 by Robert Pearman fr
    New in this version:
    - A SearchBase is required.
    - When logging, the CSV will always be overwritten.
    - Accounts with recently expired passwords can be notified by specifying a "negativedays" value.
    - Accounts with recently-expired passwords can be notified by specifying a "negativedays" value.
    - Email attempts will handle basic errors.
    - Accounts with MaxPasswordAge 00:00:00 (never) are skipped. (Same as PasswordNeverExpires.)
    - Testing-mode will allow a specified number of sample notifications to be emailed to the Administrator(s). (Rather than defaulting to all expirations.)
  14. meoso revised this gist Apr 12, 2018. 1 changed file with 3 additions and 1 deletion.
    4 changes: 3 additions & 1 deletion Password-Expiration-Notifications.ps1
    Original file line number Diff line number Diff line change
    @@ -41,8 +41,10 @@ if ( $sampleEmails -isNot [int]) {
    } #else use the value given
    }

    if ($testing -eq $true) {
    if (($testing -eq $true) -and ($sampleEmails -ge 0)) {
    Write-Host "Testing only; $sampleEmails email samples will be sent to $adminEmailAddr"
    } elseif (($testing -eq $true) -and ($sampleEmails -eq 0)) {
    Write-Host "Testing only; emails will NOT be sent"
    }

    # Create CSV Log
  15. meoso revised this gist Mar 9, 2017. 1 changed file with 1 addition and 3 deletions.
    4 changes: 1 addition & 3 deletions Password-Expiration-Notifications.ps1
    Original file line number Diff line number Diff line change
    @@ -41,10 +41,8 @@ if ( $sampleEmails -isNot [int]) {
    } #else use the value given
    }

    if (($testing -eq $true) -and ($sampleEmails -ge 0)) {
    if ($testing -eq $true) {
    Write-Host "Testing only; $sampleEmails email samples will be sent to $adminEmailAddr"
    } elseif (($testing -eq $true) -and ($sampleEmails -eq 0)) {
    Write-Host "Testing only; emails will NOT be sent"
    }

    # Create CSV Log
  16. meoso revised this gist Mar 1, 2017. No changes.
  17. meoso revised this gist Mar 1, 2017. 1 changed file with 2 additions and 1 deletion.
    3 changes: 2 additions & 1 deletion Password-Expiration-Notifications.ps1
    Original file line number Diff line number Diff line change
    @@ -199,13 +199,14 @@ Write-Host "$countsent Emails Sent."
    Write-Host "$countnotsent Emails skipped."
    Write-Host "$countfailed Emails failed."

    # sort the CSV file
    if ($logging -eq $true) {
    #sort the CSV file
    Rename-Item $logfile "$logfile.old"
    import-csv "$logfile.old" | sort ExpiresOn | export-csv $logfile -NoTypeInformation
    Remove-Item "$logFile.old"
    Write-Host "CSV File created at ${logfile}."

    #email the CSV and stats to admin(s)
    if ($testing -eq $true) {
    $body="<b><i>Testing Mode.</i></b><br>"
    } else {
  18. meoso revised this gist Mar 1, 2017. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion Password-Expiration-Notifications.ps1
    Original file line number Diff line number Diff line change
    @@ -164,7 +164,7 @@ foreach ($user in $users) {
    $countnotsent++
    }
    } else {
    Write-Host "`n$dName ($sName) has no email address."
    Write-Host "$dName ($sName) has no email address."
    $sent = "No addr"
    $countnotsent++
    }
  19. meoso revised this gist Mar 1, 2017. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion Password-Expiration-Notifications.ps1
    Original file line number Diff line number Diff line change
    @@ -1,7 +1,7 @@
    #################################################################################################################
    #
    # Password-Expiration-Notifications v20170301
    # Highly Modified fork. https://github.com/meoso
    # Highly Modified fork. https://gist.github.com/meoso/3488ef8e9c77d2beccfd921f991faa64
    #
    # Originally from v1.4 @ https://gallery.technet.microsoft.com/Password-Expiry-Email-177c3e27
    # Robert Pearman (WSSMB MVP)
  20. meoso revised this gist Mar 1, 2017. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions Password-Expiration-Notifications.ps1
    Original file line number Diff line number Diff line change
    @@ -119,9 +119,9 @@ foreach ($user in $users) {
    $body="
    <p>Your Active Directory password for your <b>$sName</b> account $messageDays. After expired, you will not be able to login until your password is changed.</p>
    <p>Please visit selfservice.example.com to change your password.</p>
    <p>Please visit selfservice.example.com to change your password. Alternatively, on a Windows machine, you may press Ctrl-Alt-Del and select `"Change Password`".</p>
    <p>If you do not know your current password, <a href='https://selfservice.example.com/?action=sendtoken'>click here to email a password reset link</a>. The email containing the reset link will be sent to your email address.</p>
    <p>If you do not know your current password, <a href='https://selfservice.example.com/?action=sendtoken'>click here to email a password reset link</a>.</p>
    <p>Thank you,<br>
    Example.com Administrator<br>
  21. meoso revised this gist Mar 1, 2017. 1 changed file with 13 additions and 0 deletions.
    13 changes: 13 additions & 0 deletions Readme.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,13 @@
    Password-Expiration-Notifications.ps1 is a powerShell script designed to be run on a schedule to automatically email Active Directory users of upcoming password expirations and recently expired passwords.

    This version is a highly modified fork of the original v1.4 by Robert Pearman from https://gallery.technet.microsoft.com/Password-Expiry-Email-177c3e27

    New in this version:
    - A SearchBase is required.
    - When logging, the CSV will always be overwritten.
    - Accounts with recently expired passwords can be notified by specifying a "negativedays" value.
    - Email attempts will handle basic errors.
    - Accounts with MaxPasswordAge 00:00:00 (never) are skipped. (Same as PasswordNeverExpires.)
    - Testing-mode will allow a specified number of sample notifications to be emailed to the Administrator(s). (Rather than defaulting to all expirations.)
    - Processing information and basic statistics are written to console.
    - When logging, the CSV file and basic statistics will be emailed to the specified Administrator(s).
  22. meoso created this gist Mar 1, 2017.
    235 changes: 235 additions & 0 deletions Password-Expiration-Notifications.ps1
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,235 @@
    #################################################################################################################
    #
    # Password-Expiration-Notifications v20170301
    # Highly Modified fork. https://github.com/meoso
    #
    # Originally from v1.4 @ https://gallery.technet.microsoft.com/Password-Expiry-Email-177c3e27
    # Robert Pearman (WSSMB MVP)
    # TitleRequired.com
    # Script to Automated Email Reminders when Users Passwords due to Expire.
    #
    # Requires: Windows PowerShell Module for Active Directory
    #
    ##################################################################################################################
    # Please Configure the following variables....
    $SearchBase="DC=EXAMPLE,DC=COM"
    $smtpServer="smtp.example.com"
    $expireindays = 7 #number of days prior notice to email notification
    $negativedays = -3 #negative number of days (days already expired) to email notification
    $from = "Administrator <[email protected]>"
    $logging = $true # Set to $false to Disable Logging
    $logNonExpiring = $false
    $logFile = "c:\PS-pwd-expiry.csv" # ie. c:\mylog.csv
    $testing = $true # Set to $false to Email Users
    $adminEmailAddr = "[email protected]","[email protected]","[email protected]" #multiple addr allowed but MUST be independent strings separated by comma
    $sampleEmails = 1 #number of sample email to send to adminEmailAddr when testing ; in the form $sampleEmails="ALL" or $sampleEmails=[0..X] e.g. $sampleEmails=0 or $sampleEmails=3 or $sampleEmails="all" are all valid.
    #
    ###################################################################################################################

    # System Settings
    $textEncoding = [System.Text.Encoding]::UTF8
    $date = Get-Date -format yyyy-MM-dd

    $starttime=Get-Date #need time also; don't use date from above

    Write-Host "Processing `"$SearchBase`" for Password-Expiration-Notifications"

    #set max sampleEmails to send to $adminEmailAddr
    if ( $sampleEmails -isNot [int]) {
    if ( $sampleEmails.ToLower() -eq "all") {
    $sampleEmails=$users.Count
    } #else use the value given
    }

    if (($testing -eq $true) -and ($sampleEmails -ge 0)) {
    Write-Host "Testing only; $sampleEmails email samples will be sent to $adminEmailAddr"
    } elseif (($testing -eq $true) -and ($sampleEmails -eq 0)) {
    Write-Host "Testing only; emails will NOT be sent"
    }

    # Create CSV Log
    if ($logging -eq $true) {
    #Always purge old CSV file
    Out-File $logfile
    Add-Content $logfile "`"Date`",`"SAMAccountName`",`"DisplayName`",`"Created`",`"PasswordSet`",`"DaystoExpire`",`"ExpiresOn`",`"EmailAddress`",`"Notified`""
    }

    # Get Users From AD who are Enabled, Passwords Expire
    Import-Module ActiveDirectory
    $users = get-aduser -SearchBase $SearchBase -Filter {(enabled -eq $true) -and (passwordNeverExpires -eq $false)} -properties sAMAccountName, displayName, PasswordNeverExpires, PasswordExpired, PasswordLastSet, EmailAddress, lastLogon, whenCreated
    $DefaultmaxPasswordAge = (Get-ADDefaultDomainPasswordPolicy).MaxPasswordAge

    $countprocessed=${users}.Count
    $samplesSent=0
    $countsent=0
    $countnotsent=0
    $countfailed=0

    # Process Each User for Password Expiry
    foreach ($user in $users) {
    $dName = $user.displayName
    $sName = $user.sAMAccountName
    $emailaddress = $user.emailaddress
    $whencreated = $user.whencreated
    $passwordSetDate = $user.PasswordLastSet
    $sent = "" # Reset Sent Flag

    $PasswordPol = (Get-AduserResultantPasswordPolicy $user)
    # Check for Fine Grained Password
    if (($PasswordPol) -ne $null) {
    $maxPasswordAge = ($PasswordPol).MaxPasswordAge
    } else {
    # No FGPP set to Domain Default
    $maxPasswordAge = $DefaultmaxPasswordAge
    }

    #If maxPasswordAge=0 then same as passwordNeverExpires, but PasswordCannotExpire bit is not set
    if ($maxPasswordAge -eq 0) {
    Write-Host "$sName MaxPasswordAge = $maxPasswordAge (i.e. PasswordNeverExpires) but bit not set."
    }

    $expiresOn = $passwordsetdate + $maxPasswordAge
    $today = (get-date)

    if ( ($user.passwordexpired -eq $false) -and ($maxPasswordAge -ne 0) ) { #not Expired and not PasswordNeverExpires
    $daystoexpire = (New-TimeSpan -Start $today -End $expiresOn).Days
    } elseif ( ($user.passwordexpired -eq $true) -and ($passwordSetDate -ne $null) -and ($maxPasswordAge -ne 0) ) { #if expired and passwordSetDate exists and not PasswordNeverExpires
    # i.e. already expired
    $daystoexpire = -((New-TimeSpan -Start $expiresOn -End $today).Days)
    } else {
    # i.e. (passwordSetDate = never) OR (maxPasswordAge = 0)
    $daystoexpire="NA"
    #continue #"continue" would skip user, but bypass any non-expiry logging
    }

    #Write-Host "$sName DtE: $daystoexpire MPA: $maxPasswordAge" #debug

    # Set verbiage based on Number of Days to Expiry.
    Switch ($daystoexpire) {
    {$_ -ge $negativedays -and $_ -le "-1"} {$messageDays = "has expired"}
    "0" {$messageDays = "will expire today"}
    "1" {$messageDays = "will expire in 1 day"}
    default {$messageDays = "will expire in " + "$daystoexpire" + " days"}
    }

    # Email Subject Set Here
    $subject="Your password $messageDays"

    # Email Body Set Here, Note You can use HTML, including Images.
    $body="
    <p>Your Active Directory password for your <b>$sName</b> account $messageDays. After expired, you will not be able to login until your password is changed.</p>
    <p>Please visit selfservice.example.com to change your password.</p>
    <p>If you do not know your current password, <a href='https://selfservice.example.com/?action=sendtoken'>click here to email a password reset link</a>. The email containing the reset link will be sent to your email address.</p>
    <p>Thank you,<br>
    Example.com Administrator<br>
    [email protected]<br>
    www.example.com/support/<br>
    </p>
    "

    # If testing-enabled and send-samples, then set recipient to adminEmailAddr else user's EmailAddress
    if (($testing -eq $true) -and ($samplesSent -lt $sampleEmails)) {
    $recipient = $adminEmailAddr
    } else {
    $recipient = $emailaddress
    }

    #if in trigger range, send email
    if ( ($daystoexpire -ge $negativedays) -and ($daystoexpire -lt $expireindays) -and ($daystoexpire -ne "NA") ) {
    # Send Email Message
    if (($emailaddress) -ne $null) {
    if ( ($testing -eq $false) -or (($testing -eq $true) -and ($samplesSent -lt $sampleEmails)) ) {
    try {
    Send-Mailmessage -smtpServer $smtpServer -from $from -to $recipient -subject $subject -body $body -bodyasHTML -priority High -Encoding $textEncoding -ErrorAction Stop -ErrorVariable err
    } catch {
    write-host "Error: Could not send email to $recipient via $smtpServer"
    $sent = "Send fail"
    $countfailed++
    } finally {
    if ($err.Count -eq 0) {
    write-host "Sent email for $sName to $recipient"
    $countsent++
    if ($testing -eq $true) {
    $samplesSent++
    $sent = "toAdmin"
    } else { $sent = "Yes" }
    }
    }
    } else {
    Write-Host "Testing mode: skipping email to $recipient"
    $sent = "No"
    $countnotsent++
    }
    } else {
    Write-Host "`n$dName ($sName) has no email address."
    $sent = "No addr"
    $countnotsent++
    }

    # If Logging is Enabled Log Details
    if ($logging -eq $true) {
    Add-Content $logfile "`"$date`",`"$sName`",`"$dName`",`"$whencreated`",`"$passwordSetDate`",`"$daystoExpire`",`"$expireson`",`"$emailaddress`",`"$sent`""
    }
    } else {
    #if ( ($daystoexpire -eq "NA") -and ($maxPasswordAge -eq 0) ) { Write-Host "$sName PasswordNeverExpires" } elseif ($daystoexpire -eq "NA") { Write-Host "$sName PasswordNeverSet" } #debug
    # Log Non Expiring Password
    if ( ($logging -eq $true) -and ($logNonExpiring -eq $true) ) {
    if ($maxPasswordAge -eq 0 ) {
    $sent = "NeverExp"
    } else {
    $sent = "No"
    }
    Add-Content $logfile "`"$date`",`"$sName`",`"$dName`",`"$whencreated`",`"$passwordSetDate`",`"$daystoExpire`",`"$expireson`",`"$emailaddress`",`"$sent`""
    }
    }

    } # End User Processing

    $endtime=Get-Date
    $totaltime=($endtime-$starttime).TotalSeconds
    $minutes="{0:N0}" -f ($totaltime/60)
    $seconds="{0:N0}" -f ($totaltime%60)

    Write-Host "$countprocessed Users from `"$SearchBase`" Processed in $minutes minutes $seconds seconds."
    Write-Host "Email trigger range from $negativedays (past) to $expireindays (upcoming) days of user's password expiry date."
    Write-Host "$countsent Emails Sent."
    Write-Host "$countnotsent Emails skipped."
    Write-Host "$countfailed Emails failed."

    # sort the CSV file
    if ($logging -eq $true) {
    Rename-Item $logfile "$logfile.old"
    import-csv "$logfile.old" | sort ExpiresOn | export-csv $logfile -NoTypeInformation
    Remove-Item "$logFile.old"
    Write-Host "CSV File created at ${logfile}."

    if ($testing -eq $true) {
    $body="<b><i>Testing Mode.</i></b><br>"
    } else {
    $body=""
    }

    $body+="
    CSV Attached for $date<br>
    $countprocessed Users from `"$SearchBase`" Processed in $minutes minutes $seconds seconds.<br>
    Email trigger range from $negativedays (past) to $expireindays (upcoming) days of user's password expiry date.<br>
    $countsent Emails Sent.<br>
    $countnotsent Emails skipped.<br>
    $countfailed Emails failed.
    "

    try {
    Send-Mailmessage -smtpServer $smtpServer -from $from -to $adminEmailAddr -subject "Password Expiry Logs" -body $body -bodyasHTML -Attachments "$logFile" -priority High -Encoding $textEncoding -ErrorAction Stop -ErrorVariable err
    } catch {
    write-host "Error: Failed to email CSV log to $adminEmailAddr via $smtpServer"
    } finally {
    if ($err.Count -eq 0) {
    write-host "CSV emailed to $adminEmailAddr"
    }
    }
    }

    # End