I found a really nice password expire notification script on the net at http://www.ehloworld.com/318. I decided to test it inside of a scheduled tasks and it works . However it continues to loop through the script sending email's to the soon to expire users. I would use the built in version within Adaxes, but it seemed lacking and we had already been using this as a scheduled windows task on a utility server. Any help would be greatly appreciated on what it is i could do to stop it from looping.
[cmdletBinding(SupportsShouldProcess = $true)]
param(
[parameter(ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $true, Mandatory = $false)]
[switch]$Demo,
[parameter(ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $true, Mandatory = $false)]
[switch]$Install,
[parameter(ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $true, Mandatory = $false)]
[switch]$Preview,
[parameter(ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $true, Mandatory = $false)]
[string]$PreviewUser,
[parameter(ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $true, Mandatory = $false)]
[bool]$NoImages = $true
)
Write-Verbose "Setting variables"
[string]$Company = "Company"
[string]$OwaUrl = "OWA URL"
[string]$PSEmailServer = "IP Address"
[string]$EmailFrom = "Help Desk Email"
[string]$HelpDeskPhone = "Phone Number"
[string]$HelpDeskURL = "Help Desk"
[string]$TranscriptFilename = $MyInvocation.MyCommand.Name + " " + $env:ComputerName + " {0:yyyy-MM-dd hh-mmtt}.log" -f (Get-Date)
[int]$global:UsersNotified = 0
[int]$DaysToWarn = 14
[string]$ImagePath = "http://website.com"
[string]$ScriptName = $MyInvocation.MyCommand.Name
[string]$ScriptPathAndName = $MyInvocation.MyCommand.Definition
[string]$ou
[string]$DateFormat = "d"
if ($PreviewUser){
$Preview = $true
}
Write-Verbose "Defining functions"
function Set-ModuleStatus {
[cmdletBinding(SupportsShouldProcess = $true)]
param (
[parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Mandatory = $true, HelpMessage = "No module name specified!")]
[string]$name
)
if(!(Get-Module -name "$name")) {
if(Get-Module -ListAvailable | ? {$_.name -eq "$name"}) {
Import-Module -Name "$name"
# module was imported
return $true
} else {
# module was not available
return $false
}
}else {
# module was already imported
return $true
}
} # end function Set-ModuleStatus
function Remove-ScriptVariables {
[cmdletBinding(SupportsShouldProcess = $true)]
param(
[string]$path
)
$result = Get-Content $path |
ForEach {
if ( $_ -match '(\$.*?)\s*=') {
$matches[1] | ? { $_ -notlike '*.*' -and $_ -notmatch 'result' -and $_ -notmatch 'env:'}
}
}
ForEach ($v in ($result | Sort-Object | Get-Unique)){
Remove-Variable ($v.replace("$","")) -EA 0
}
} # end function Remove-ScriptVariables
function Install {
[cmdletBinding(SupportsShouldProcess = $true)]
param()
$error.clear()
Write-Host "Creating scheduled task `"$ScriptName`"..."
$TaskCreds = Get-Credential("$env:userdnsdomain\$env:username")
$TaskPassword = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($TaskCreds.Password))
schtasks /create /tn $ScriptName /tr "$env:windir\system32\windowspowershell\v1.0\powershell.exe -command $ScriptPathAndName" /sc Daily /st 06:00 /ru $TaskCreds.UserName /rp $TaskPassword | Out-Null
if (! $error){
Write-Host "Installation complete!" -ForegroundColor green
}else{
Write-Host "Installation failed!" -ForegroundColor red
}
remove-variable taskpassword
exit
} # end function Install
function Get-ADUserPasswordExpirationDate {
[cmdletBinding(SupportsShouldProcess = $true)]
Param (
[Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true, HelpMessage = "Identity of the Account")]
[Object]$accountIdentity
)
PROCESS {
Write-Verbose "Getting the user info for $accountIdentity"
$accountObj = Get-ADUser $accountIdentity -properties PasswordExpired, PasswordNeverExpires, PasswordLastSet, name, mail
# Make sure the password is not expired...is the account de-provisioned?
Write-Verbose "verifying that the password is not expired, and the user is not set to PasswordNeverExpires"
if (((!($accountObj.PasswordExpired)) -and (!($accountObj.PasswordNeverExpires))) -or ($PreviewUser)) {
Write-Verbose "Verifying if the date the password was last set is available"
$passwordSetDate = $accountObj.PasswordLastSet
if ($passwordSetDate -ne $null) {
$maxPasswordAgeTimeSpan = $null
Write-Verbose "Determining domain functional level"
if ($global:dfl -ge 4) { # 2008 Domain functional level
$accountFGPP = Get-ADUserResultantPasswordPolicy $accountObj
if ($accountFGPP -ne $null) {
$maxPasswordAgeTimeSpan = $accountFGPP.MaxPasswordAge
} else {
$maxPasswordAgeTimeSpan = (Get-ADDefaultDomainPasswordPolicy).MaxPasswordAge
}
} else { # 2003 or ealier Domain Functional Level
$maxPasswordAgeTimeSpan = (Get-ADDefaultDomainPasswordPolicy).MaxPasswordAge
}
if ($maxPasswordAgeTimeSpan -eq $null -or $maxPasswordAgeTimeSpan.TotalMilliseconds -ne 0) {
$DaysTillExpire = [math]::round(((New-TimeSpan -Start (Get-Date) -End ($passwordSetDate + $maxPasswordAgeTimeSpan)).TotalDays),0)
if ($preview){$DaysTillExpire = 1}
if ($DaysTillExpire -le $DaysToWarn){
Write-Verbose "User should receive email"
$PolicyDays = [math]::round((($maxPasswordAgeTimeSpan).TotalDays),0)
if ($demo) {Write-Host ("{0,-25}{1,-8}{2,-12}" -f $accountObj.Name, $DaysTillExpire, $PolicyDays)}
# start assembling email to user here....grab this script from technet
$EmailName = $accountObj.Name
$DateofExpiration = (Get-Date).AddDays($DaysTillExpire)
$DateofExpiration = (Get-Date($DateofExpiration) -f $DateFormat)
Write-Verbose "Assembling email message"
[string]$emailbody = @"
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<style type="text/css">
.auto-style1 {
color: #FF0000;
}
</style>
</head>
<body>
"@
if (!($NoImages)){
$emailbody += @"
<table id="email" border="0" cellspacing="0" cellpadding="0" width="655" align="center">
<tr>
<td align="left" valign="top"><img src="$ImagePath/spacer.gif" alt="Description: $ImagePath/spacer.gif" width="46" height="28" align="absMiddle">
</td>
</tr>
"@
if ($HelpDeskURL){
$emailbody += @"
<tr><td height="121" align="left" valign="bottom"><a href="$HelpDeskURL"><img src="$ImagePath/header.gif" border="0" alt="Description: $ImagePath/header.gif" width="655" height="121"></a></td></tr>
"@
}else{
$emailbody += @"
<tr><td height="121" align="left" valign="bottom"><img src="$ImagePath/header.gif" border="0" alt="Description: $ImagePath/header.gif" width="655" height="121"></td></tr>
"@
}
$emailbody += @"
<tr>
<td>
<table id="body" border="0" cellspacing="0" cellpadding="0">
<tr>
<td width="1" align="left" valign="top" bgcolor="#a8a9ad"><img src="$ImagePath/spacer50.gif" alt="Description: $ImagePath/spacer50.gif" width="1" height="50"></td>
<td><img src="$ImagePath/spacer.gif" alt="Description: $ImagePath/spacer.gif" width="46" height="106"></td>
<td id="text" width="572" align="left" valign="top" style="font-size: 12px; color: #000000; line-height: 17px; font-family: Verdana, Arial, Helvetica, sans-serif">
"@
}
if ($DaysTillExpire -le 1){
$emailbody += @"
<div align='center'>
<table border='0' cellspacing='0' cellpadding='0' style='width:510px; background-color: white; border: 0px;'>
<tr>
"@
if (!($NoImages)){
$emailbody += @"
<td align='right'><img width='36' height='28' src='$ImagePath/image001b.gif' alt='Description: $ImagePath/image001b.gif'></td>
"@
}
$emailbody += @"
<td style="font-family: verdana; background: #E12C10; text-align: center; padding: 0px; font-size: 9.0pt; color: white">ALERT: You must change your password today or you will be locked out!</td>
"@
if (!($NoImages)){
$emailbody += @"
<td align='left'><img border='0' width='14' height='28' src='$ImagePath/image005b.gif' alt='Description: $ImagePath/image005b.gif'></td>
"@
}
$emailbody += @"
</tr>
</table>
</div>
"@
}
$emailbody += @"
<p style="font-weight: bold">Hello, $EmailName,</p>
<p>It's password changing time again! Your $company password expires in <span style="background-color: red; color: white; font-weight: bold;"> $DaysTillExpire </span> day(s), on $DateofExpiration.</p>
<p> </p>
<p>Please use one of the methods below to update your password:</p>
<ol>
<li>$company office computers: You may update your password on your computer by pressing Ctrl-Alt-Delete and selecting 'Change
a Password...' from the available options. If you use a $company laptop in addition to a desktop PC, be sure and read #3 below.</li>
<li>Remote Outlook Client, Mac, and/or Outlook Web App users: If you only access our email system, please use the following method to easily change your password:</li>
<ul>
<li>Log into <a href="$owaurl">Outlook Web App</a> using Internet Explorer (PC) or
Safari or Firefox.</li>
<li>Click on the Options button in the upper right corner of the page.</li>
<li>Select the "Change your Password" link to change your password.</li>
<li>Enter your current password, then your new password twice, and click Save</li>
</ul>
<li>$company issued laptops/tablets: If you have been issued a $company laptop,
or tablet you must be in a corporate office or directly connected to the company
over VPN network to change your password. If you also use a desktop PC in the office, you must remember to always update your domain password on the laptop/tablet first. Your desktop will automatically use the new password.</li>
<ul>
<li>Log in on laptop/tablet</li>
<li>Press Ctrl-Alt-Delete and select 'Change a Password...' from the available options.</li>
<li>Make sure your workstation (if you have one) has been logged off any previous sessions so as to not cause conflict with your new password.</li>
</ul>
</ol>
<p><span class="auto-style1">NOTE:</span> You will now
need to use your new password when logging into Outlook
Web App, Outlook 2010, SharePoint, Windows Mobile
(ActiveSync) devices, etc. If you have been assigned an
iPhone or iPad you will need to update each device with
your new password. Blackberry Enterprise Users (BES)
will not need to update their password.</p>
<ol>
<li>
<span style="font-size:11.0pt;font-family:"Calibri","sans-serif";
mso-fareast-font-family:Calibri;mso-fareast-theme-font:minor-latin;mso-ansi-language:
EN-US;mso-fareast-language:EN-US;mso-bidi-language:AR-SA">To update the password
in your iPhone or iPad do the following:</span><ul>
<li>Go to Settings then select Mail, Contacts,
Calendars, this will list all email accounts
synced to your device.</li>
<li>Select Exchange, which is your SAM Inc email
account, and then touch the Account field with
your email address to edit credentials.</li>
<li>Clear then update the password field with
your new password and select done. A checkmark
to the right of each field will verify your
password has been accepted and updated.</li>
</ul>
<p class="MsoNormal"> </p>
</li>
</ol>
<p>Think you've got a complex password? Run it through the <a href="http://www.passwordmeter.com/">The Password Meter</a></p>
<p>Think your password couldn't easily be hacked? See how long it would take: <a href="http://howsecureismypassword.net/">How Secure Is My Password</a></p>
<p>Remember, if you do not change your password before it expires on $DateofExpiration, you will be locked out of all $company Computer Systems until an Administrator unlocks your account.</p>
<p>If you are traveling or will not be able to bring your laptop into the office before your password expires, please call the number below for additional instructions.</p>
<p> You will continue to receive these emails daily until the password is changed or expires.</p>
<p> </p>
<p>Thank you,<br />
The $company Help Desk<br />
$HelpDeskPhone</p>
"@
if ($accountFGPP -eq $null){
$emailbody += @"
<table style="background-color: #dedede; border: 1px solid black">
<tr>
<td style="font-size: 12px; color: #000000; line-height: 17px; font-family: Verdana, Arial, Helvetica, sans-serif"><b>$company Password Policy</b>
<ul>
<li>Your password must have a minimum of a $MinPasswordLength characters.</li>
<li>You may not use a previous password.</li>
<li>Your password must not contain parts of your first, last, or logon name.</li>
<li>Your password must be changed every $PolicyDays days.</li>
"@
if ($PasswordComplexity){
Write-Verbose "Password complexity"
$emailbody += @"
<li>Your password requires a minimum of two of the following three categories:</li>
<ul>
<li>1 upper case character (A-Z)</li>
<li>1 lower case character (a-z)</li>
<li>1 numeric character (0-9)</li>
</ul>
"@
}
$emailbody += @"
<li>You may not reuse any of your last $PasswordHistory passwords</li>
</ul>
</td>
</tr>
</table>
"@
}
if (!($NoImages)){
$emailbody += @"
</td>
<td width="49" align="left" valign="top"><img src="$ImagePath/spacer50.gif" alt="" width="49" height="50"></td>
<td width="1" align="left" valign="top" bgcolor="#a8a9ad"><img src="$ImagePath/spacer50.gif" alt="Description: $ImagePath/spacer50.gif" width="1" height="50"></td>
</tr>
</table>
<table id="footer" border="0" cellspacing="0" cellpadding="0" width="655">
<tr>
<td><img src="$ImagePath/footer.gif" alt="Description: $ImagePath/footer.gif" width="655" height="81"></td>
</tr>
</table>
<table border="0" cellspacing="0" cellpadding="0" width="655" align="center">
<tr>
<td align="left" valign="top"><img src="$ImagePath/spacer.gif" alt="Description: $ImagePath/spacer.gif" width="36" height="1"></td>
<td align="middle" valign="top"><font face="Verdana" size="1" color="#000000"><p>This email was sent by an automated process.
"@
}
if ($HelpDeskURL){
$emailbody += @"
If you would like to comment on it, please visit <a href="$HelpDeskURL"><font color="#ff0000"><u>click here</u></font></a>
"@
}
if (!($NoImages)){
$emailbody += @"
</p></font>
</td>
<td align="left" valign="top"><img src="$ImagePath/spacer.gif" alt="Description: $ImagePath/spacer.gif" width="36" height="1"></td>
</tr>
</table>
</td>
</tr>
</table>
"@
}
$emailbody += @"
</body>
</html>
"@
if (!($demo)){
$emailto = $accountObj.mail
if ($emailto){
Write-Verbose "Sending demo message to $emailto"
Send-MailMessage -To $emailto -Subject "Your password expires in $DaysTillExpire day(s)" -Body $emailbody -From $EmailFrom -Priority High -BodyAsHtml
$global:UsersNotified++
}else{
Write-Verbose "Can not email this user. Email address is blank"
}
}
}
}
}
}
}
} # end function Get-ADUserPasswordExpirationDate
if ($install){
Write-Verbose "Install mode"
Install
Exit
}
Write-Verbose "Checking for ActiveDirectory module"
if ((Set-ModuleStatus ActiveDirectory) -eq $false){
$error.clear()
Write-Host "Installing the Active Directory module..." -ForegroundColor yellow
Set-ModuleStatus ServerManager
Add-WindowsFeature RSAT-AD-PowerShell
if ($error){
Write-Host "Active Directory module could not be installed. Exiting..." -ForegroundColor red;
if ($transcript){Stop-Transcript}
exit
}
}
Write-Verbose "Getting Domain functional level"
$global:dfl = (Get-AdDomain).DomainMode
# Get-ADUser -filter * -properties PasswordLastSet,EmailAddress,GivenName -SearchBase "OU=Users,DC=domain,DC=test" |foreach {
if (!($PreviewUser)){
if ($ou){
Write-Verbose "Filtering users to $ou"
$users = Get-AdUser -filter * -SearchScope subtree -SearchBase $ou -ResultSetSize $null
}else{
$users = Get-AdUser -filter * -ResultSetSize $null
}
}else{
Write-Verbose "Preview mode"
$users = Get-AdUser $PreviewUser
}
if ($demo){
Write-Verbose "Demo mode"
# $WhatIfPreference = $true
Write-Host "`n"
Write-Host ("{0,-25}{1,-8}{2,-12}" -f "User", "Expires", "Policy") -ForegroundColor cyan
Write-Host ("{0,-25}{1,-8}{2,-12}" -f "========================", "=======", "===========") -ForegroundColor cyan
}
Write-Verbose "Setting event log configuration"
[object]$evt = new-object System.Diagnostics.EventLog("Application")
[string]$evt.Source = $ScriptName
$infoevent = [System.Diagnostics.EventLogEntryType]::Information
[string]$EventLogText = "Beginning processing"
$evt.WriteEntry($EventLogText,$infoevent,70)
Write-Verbose "Getting password policy configuration"
$DefaultDomainPasswordPolicy = Get-ADDefaultDomainPasswordPolicy
[int]$MinPasswordLength = $DefaultDomainPasswordPolicy.MinPasswordLength
# this needs to look for FGPP, and then default to this if it doesn't exist
[bool]$PasswordComplexity = $DefaultDomainPasswordPolicy.ComplexityEnabled
[int]$PasswordHistory = $DefaultDomainPasswordPolicy.PasswordHistoryCount
ForEach ($user in $users){
Get-ADUserPasswordExpirationDate $user.samaccountname
}
# $WhatIfPreference = $false
Remove-ScriptVariables -path $ScriptPathAndName