I've been working on a similar script, I did not test it extensively but
give it a try, you'll need Quest AD cmdlets (http://www.quest.com/powershell/activeroles-server.aspx).
If you want to run it against a specific OU, add the -ou parameter (alias
for searchRoot) to Get-QADUser with the value of the OU DN (e.g Get-QADUser
-ou <ou_dn> ...):
# get domain maximumPasswordAge password policy
$maximumPasswordAge = (Get-QADObject (Get-QADRootDSE).defaultNamingContextDN).maximumPasswordAge.value.days
if(!$maximumPasswordAge){
throw "Domain 'MaximumPasswordAge'password policy is not configured (set
to 0)."
}
# exclude users with no expiring password or cannot change password
$ldap = "(!userAccountControl:1.2.840.113556.1.4.803:=65536)(!userAccountControl:1.2.840.113556.1.4.803:=64)"
# create calculated property to display days until password expire
$daysUntilExpire = @{n="daysUntilExpire";e={$maximumPasswordAge-$_.passwordAge.value.days}}
$expireIn = 10
# get enabled users that meet the above criteria
Get-QADUser -enabled -size 0 -ldap $ldap | where {$_.passwordAge.value -gt
0 -AND ($maximumPasswordAge-$_.passwordAge.value.days) -gt $expireIn} | select
name,email,passwordAge,$daysUntilExpire
---
Shay Levy
Windows PowerShell MVP
http://blogs.microsoft.co.il/blogs/ScriptFanatic
PowerShell Toolbar: http://tinyurl.com/PSToolbar
EJ> How can I use a script to determine password-expiration dates for
EJ> users in a organizational unit (OU) and send an email message to
EJ> accounts whose passwords expire soon? For example, if my password
EJ> expires after 14 days, then it sends me email with that info.
EJ>
**First we need to know the maximum password age for the domain:
function Get-maxPwdAge{
$root = [ADSI]"LDAP://acf.americredit.com"
$filter = "(&(objectcategory=domainDNS)
(distinguishedName=DC=acf,DC=americredit,DC=com))"
$ds = New-Object system.DirectoryServices.DirectorySearcher($root,
$filter)
$dc = $ds.findone()
[int64]$mpa = ($dc.Properties[‘maxpwdage’][0]).ToString().Trim
("-")
$mpa*(.000000100)/86400
}
**Then we can take this information to calculate the current password
age of a user object:
[int]$maxpwdage = Get-maxPwdAge
[int]$gracedays = 14
[int]$days = $maxpwdage - $gracedays
$old = (Get-Date).AddDays(-$days) #Max Pwd Age - Lead Time (60 - 14)
$users = Get-QADUser -SearchRoot "OU=Users,DC=domain,DC=com"
write-Host "Creating Password Expiration Report. Please
wait.........."
$report = $users | foreach {$users | where {$users.pwdLastSet -le $old
-and $users.pwdLastSet -gt "1/1/1601" $users.objectclass -eq "user"} |
select name,email,pwdlastset}
$report | Export-Csv $reportfile -noType
$dt = "{0:d}" -f [datetime](get-date).adddays(-$days)
Import-Csv $reportfile | ForEach-Object {
$username = $_.Name
[datetime]$pwdset = $_.pwdLastSet
$email = $_.Email
#$daysleft = maxpwdage minus days since last change (60 - 50 = 10 days
before password expires)
$body = [string]::join([environment]::newline, (Get-Content
$emailbody ))
$daysleft = $maxpwdage - [int]((get-date).Subtract($pwdset)).days
if ($daysleft -le 0 -and $email -ne ""){
$subject = "Your login password has expired."
Send-smtpMail -smtphost smtphost -to $email -from "us...@domain.com" -
subject $subject -body $body.ToString()
Write-Host "Password for $username has Expired. A message will be sent
to $email"
}
if ($daysleft -gt 0 -and $email -ne ""){
$subject = "Your login password will expire in $daysleft days."
Send-smtpMail -smtphost smtphost -to $email -from "us...@domain.com" -
subject $subject -body $body.ToString()
Write-Host "User $username has $daysleft days left before password
expires. Message will be sent to $email."
}
}
send-smtpmail -smtphost smtphost -to "us...@domain.com" -from
"us...@domain.com" -subject "Password Report" -AttachmentPath $ProgLog
# get domain maximumPasswordAge value (it is always constant in my case)
$maximumPasswordAge = 90
# exclude users with no expiring password or cannot change password
$ldap =
"(!userAccountControl:1.2.840.113556.1.4.803:=65536)(!userAccountControl:1.2.840.113556.1.4.803:=64)"
# create calculated property to display days until password expire
$daysUntilExpire =
@{n="daysUntilExpire";e={$maximumPasswordAge-$_.passwordAge.value.days}}
# get all users
$expireIn = 0
# get enabled users that meet the above criteria
Get-QADUser -ou 'OU=MyTest,DC=domain,DC=com' -enabled -size 0 -ldap $ldap |
where {$_.passwordAge.value -gt
0 -AND ($maximumPasswordAge-$_.passwordAge.value.days) -gt $expireIn} |
select Name,email,passwordAge,$daysUntilExpire | sort daysUntilExpire
##End of script
I need the script to send email to account who's daysUntilExpire value
equals 14
#start of the script
# get domain maximumPasswordAge password policy
$maximumPasswordAge = 90
# exclude users with no expiring password or cannot change password
$ldap =
"(!userAccountControl:1.2.840.113556.1.4.803:=65536)(!userAccountControl:1.2.840.113556.1.4.803:=64)"
# create calculated property to display days until password expire
$daysUntilExpire =
@{n="daysUntilExpire";e={$maximumPasswordAge-$_.passwordAge.value.days}}
$expireIn = 0
# get enabled users that meet the above criteria
Get-QADUser -ou 'OU=MyTest,DC=domain,DC=com' -enabled -size 0 -ldap $ldap |
where {$_.passwordAge.value -gt
0 -AND ($maximumPasswordAge-$_.passwordAge.value.days) -gt $expireIn} |
select Name,email,passwordAge,$daysUntilExpire | sort daysUntilExpire
# send email to account who's password will expire in 14 days
$username = $_.Name
$email = $_.Email
$smtpServer = "mysmtpserver.domain.com"
if ($daysUntilExpire -eq 14 -and $email -ne "")
{
Write-Host "User $username has $daysUntilExpire days left before password
expires. Message will be sent to $email."
$subject = "Your login password will expire in $daysUntilExpire days."
$body = "Hello $username . Your login password will expire in
$daysUntilExpire days."
Send-smtpMail -smtphost $smtpServer -to $email -from
"passwex...@mydomain.com" -subject $subject -body $body
}
#end of the script
I wouldn't set the $maximumPasswordAge var to a fixed value, someone may
change the password policy and you wont even know. This can lead the script
to send false emails.
I've also changed the ldap query, I removed the 'no expiring password' filter
and replaced it with the -passwordNeverExpires built-in parameter.
To get users that their password expires in 14 days: -eq $expireIn
To get users that their password expires in 14 days or more: -ge $expireIn
As a side note, PowerShell CTP3 has a new cmdlet to send emails, look for
: Send-MailMessage
function Send-Mail{
param($smtpServer,$from,$to,$subject,$body)
$smtp = new-object system.net.mail.smtpClient($SmtpServer)
$mail = new-object System.Net.Mail.MailMessage
$mail.from = $from
$mail.to.add($to)
$mail.subject = $subject
$mail.body = $body
#$mail.IsBodyHtml = $true
$smtp.send($mail)
}
# get domain maximumPasswordAge password policy
$maximumPasswordAge = (Get-QADObject (Get-QADRootDSE).defaultNamingContextDN).maximumPasswordAge.value.days
if(!$maximumPasswordAge){
throw "Domain 'MaximumPasswordAge'password policy is not configured (set
to 0)."
}
# exclude users that cannot change password
$ldap = "(!userAccountControl:1.2.840.113556.1.4.803:=64)"
# create calculated property to display days until password expire
$daysUntilExpire = @{n="daysUntilExpire";e={$maximumPasswordAge-$_.passwordAge.value.days}}
$expireIn = 14
# get enabled users that meet the above criteria
$expiredUsers = Get-QADUser -enabled -passwordNeverExpires $true -size 0
-ldap $ldap | where {$_.passwordAge.value -gt 0 -AND ($maximumPasswordAge-$_.passwordAge.value.days)
-eq $expireIn}
$expiredUsers | foreach {
if($_.email)
{
$subject="Your password will expire in $expireIn days"
$body="Your password will expire in $expireIn days"
Send-Mail -smtpServer exServerName -from "y...@domain.com" -to $_.email
-subject $subject -body $body
}
else
{
write-warning "user $($_.name) has no email address"
}
}
}
}
---
Shay Levy
Windows PowerShell MVP
http://blogs.microsoft.co.il/blogs/ScriptFanatic
PowerShell Toolbar: http://tinyurl.com/PSToolbar
EJ> OK i managed to write this, displaying works great but it does not
EJ> send emails - any ideas whats wrong with the email sending part of
EJ> the script?
EJ>
EJ> #start of the script
EJ>
EJ> # get domain maximumPasswordAge password policy
EJ> $maximumPasswordAge = 90
EJ> # exclude users with no expiring password or cannot change password
EJ>
EJ> $ldap =
EJ>
EJ> "(!userAccountControl:1.2.840.113556.1.4.803:=65536)(!userAccountCon
EJ> trol:1.2.840.113556.1.4.803:=64)"
EJ>
EJ> # create calculated property to display days until password expire
EJ> $daysUntilExpire =
EJ> @{n="daysUntilExpire";e={$maximumPasswordAge-$_.passwordAge.value.da
EJ> ys}}
EJ>
EJ> $expireIn = 0
EJ>
EJ> # get enabled users that meet the above criteria
EJ> Get-QADUser -ou 'OU=MyTest,DC=domain,DC=com' -enabled -size 0 -ldap
EJ> $ldap |
EJ> where {$_.passwordAge.value -gt
EJ> 0 -AND ($maximumPasswordAge-$_.passwordAge.value.days) -gt
EJ> $expireIn} |
EJ> select Name,email,passwordAge,$daysUntilExpire | sort
EJ> daysUntilExpire
EJ> # send email to account who's password will expire in 14 days
EJ> $username = $_.Name
EJ> $email = $_.Email
EJ> $smtpServer = "mysmtpserver.domain.com"
EJ> if ($daysUntilExpire -eq 14 -and $email -ne "")
EJ> {
EJ> Write-Host "User $username has $daysUntilExpire days left before
EJ> password
EJ> expires. Message will be sent to $email."
EJ> $subject = "Your login password will expire in $daysUntilExpire
EJ> days."
EJ> $body = "Hello $username . Your login password will expire in
EJ> $daysUntilExpire days."
EJ> Send-smtpMail -smtphost $smtpServer -to $email -from
EJ> "passwex...@mydomain.com" -subject $subject -body $body
EJ> }
EJ> #end of the script
EJ>
What might be the problem?
Eero
It is probably due to the post wrapping. Make sure the $expiredUsers variable assigmnet is on one line. I've also attached the code as a text file, hopefully it will be posted.
---
Shay Levy
Windows PowerShell MVP
http://blogs.microsoft.co.il/blogs/ScriptFanatic
PowerShell Toolbar: http://tinyurl.com/PSToolbar
EJ> Thank you Shay,
EJ> But right now this code gives me error:
EJ> Missing expression after unary operator '-'.
EJ> At C:\ps\passwnotify.ps1:35 char:2
EJ> + -l <<<< dap $ldap | where {$_.passwordAge.value -gt 0 -AND
EJ> ($maximumPasswordAge-$_.passwordAge.value.days)
EJ> What might be the problem?
EJ> Eero
EJ> "Shay Levy [MVP]" wrote:
EJ>
Unexpected token 'Send-Mail' in expression or statement.
At C:\ps\passwordnotify.ps1:39 char:66
+ $body="Your password will expire in $expireIn days" Send-Mail <<<<
-smtpServer "mysmtpserver.mydomain.com" -from
"passw...@mydomain.com" -to $_.email -subject $subject -body $body
Can you see what is wrong here?
Big thanks,
Eero
The Send-Mail call should be on a line of its own. The script is attached
to the previous thread. Do you see it?
---
Shay Levy
Windows PowerShell MVP
http://blogs.microsoft.co.il/blogs/ScriptFanatic
PowerShell Toolbar: http://tinyurl.com/PSToolbar
EJ> OK i removed the wraps but now i get this error:
EJ>
EJ> Unexpected token 'Send-Mail' in expression or statement.
EJ> At C:\ps\passwordnotify.ps1:39 char:66
EJ> + $body="Your password will expire in $expireIn days" Send-Mail
EJ> <<<<
EJ> -smtpServer "mysmtpserver.mydomain.com" -from
EJ> "passw...@mydomain.com" -to $_.email -subject $subject -body
EJ> $body
EJ> Can you see what is wrong here?
EJ> Big thanks,
1. I had a logic error, replace '-passwordNeverExpires $true' with '-passwordNeverExpires $false'
2. I moved the email attribute check to the ldap filter, it will get only users that have the mail attribute set (now there is no need for if/else inside the last foreach)
3. It doesn't output anything because you probably have no users with exact number of days defined in '-eq $expireIn', try to use '-ge $expireIn'
The new script is attached to this thread.
---
Shay Levy
Windows PowerShell MVP
http://blogs.microsoft.co.il/blogs/ScriptFanatic
PowerShell Toolbar: http://tinyurl.com/PSToolbar
EJ> The script works now but unfortunately everytime i run it, i always
EJ> get the
EJ> result:
EJ> WARNING: user has no email address
EJ> It cannot display username and cannot find email address.
EJ> Any ideas?
EJ> Thank you,
Thank you very much,
Eero
---
Shay Levy
Windows PowerShell MVP
http://blogs.microsoft.co.il/blogs/ScriptFanatic
PowerShell Toolbar: http://tinyurl.com/PSToolbar
EJ> For some reason, i can not find attached script anywhere. If you
EJ> can, please send it to my e-mail address eero....@emt.ee
EJ>
EJ> Thank you very much,
... | where {$_.passwordAge.value -gt 0 -AND ($maximumPasswordAge-$_.passwordAge.value.days)
with
... | where { $_.passwordAge.value.days -gt $maximumPasswordAge)
---
Shay Levy
Windows PowerShell MVP
http://blogs.microsoft.co.il/blogs/ScriptFanatic
PowerShell Toolbar: http://tinyurl.com/PSToolbar
Thanks will report !
I'm still coming to grips with hash tables so if someone has an idea for
tweaking it I'd appreciate it!
Thanks, W.
"Shay Levy [MVP]" <n...@addre.ss> wrote in message
news:89228ed260e398...@news.microsoft.com...
text file attached.
Thanks, Walt.
"Walt S" <som...@example.com> wrote in message
news:%237UlfUD...@TK2MSFTNGP05.phx.gbl...
--
lundholmster
Thank you.
--
mdbman66
Can you guys please paste the completed script?
I tried to run this, but am getting error-ed out..
Thanks
--
mdbman66
thanks
--
alexc
--
hogcruzr05
I use the script below. It is also possible to use Where-Object although
it's not as efficient as using an LDAP filter.
Chris
#
# Load Add-Ins
#
Get-PsSnapIn -Registered | Add-PsSnapIn -ErrorAction "SilentlyContinue"
#
# Constants
#
$SmtpServer = "mail.domain.com"
#
# Functions
#
Function GetUsersToNotify($MaximumAge, $DaysFromChange) {
# Finds all users in AD where the password will expire in $DaysFromChange
$StartDate = (New-TimeSpan $(Get-Date("01/01/1601 00:00:00")) `
((Get-Date).Date.AddDays(-$MaximumAge + $DaysFromChange))).Ticks
$EndDate = (New-TimeSpan $(Get-Date("01/01/1601 00:00:00")) `
((Get-Date).Date.AddDays(-$MaximumAge + $DaysFromChange + 1))).Ticks
$LdapFilter = "(&(pwdLastSet>=$StartDate)(pwdLastSet<=$EndDate))"
Get-QADUser -LdapFilter $LdapFilter `
-Enabled -IncludedProperties pwdLastSet | `
Select-Object Name, PwdLastSet, Mail
}
Function NotifyUser($Mail, $PasswordLastSet, $DaysFromChange) {
$Message = "<!DOCTYPE HTML PUBLIC `"-//W3C//DTD HTML 4.01//EN`"
`"http://www.w3.org/TR/html4/strict.dtd`">`r`n"
$Message += "<html>"
$Message += "<head>"
$Message += "<title>Password Expiry Warning</title>"
$Message += "<style type='text/css'>"
$Message += "body { font-family: verdana; font-size: 10pt; }"
$Message += "</style>"
$Message += "</head><body>"
$Message += "<p>Your password will expire in <b>$DaysFromChange</b>
days.</p>"
$Message += "<p>To change your password immediately please visit <a
href='https://mmail.domain.com/owa'>https://mail.domain.com/owa</a>.</p>"
$Message += "</body></html>"
$MailMessage = New-Object Net.Mail.MailMessage( `
"sen...@domain.com", `
$Mail, `
"Password Expiry Warning - $DaysFromChange Days", `
$Message)
$MailMessage.IsBodyHtml = $True
$SmtpClient = New-Object Net.Mail.SmtpClient($SmtpServer)
$SmtpClient.Send($MailMessage)
}
#
# Main code
#
# Maximum Password Age
$MaximumAge = (Get-QADObject -LdapFilter
"(objectClass=domainDNS)").MaximumPasswordAge.Days
# 14 days from change
GetUsersToNotify $MaximumAge 14 | %{
NotifyUser $_.Mail $_.PwdLastSet 14
}
# 7 days from change
GetUsersToNotify $MaximumAge 7 | %{
NotifyUser $_.Mail $_.PwdLastSet 7
}
# 1 day from change
GetUsersToNotify $MaximumAge 1 | %{
NotifyUser $_.Mail $_.PwdLastSet 1
}
function Send-Mail{
param($smtpServer,$from,$to,$subject,$body)
$smtp = new-object system.net.mail.smtpClient($SmtpServer)
$mail = new-object System.Net.Mail.MailMessage
$mail.from = $from
$mail.to.add($to)
$mail.subject = $subject
$mail.body = $body
#$mail.IsBodyHtml = $true
$smtp.send($mail)
}
add-pssnapin "Quest.ActiveRoles.ADManagement"
# get domain maximumPasswordAge password policy
$maximumPasswordAge = (Get-QADObject
(Get-QADRootDSE).defaultNamingContextDN).maximumPasswordAge.value.days
if(!$maximumPasswordAge){
throw "Domain 'MaximumPasswordAge'password policy is not configured
(set to 0)."
}
# exclude users that cannot change password
$ldap = "(!userAccountControl:1.2.840.113556.1.4.803:=64)(mail=*)"
# create calculated property to display days until password expire
$daysUntilExpire =
@{n="daysUntilExpire";e={$maximumPasswordAge-$_.passwordAge.value.days}}
$expireIn = 15
# get enabled users that meet the above criteria
$expiredUsers = Get-QADUser -enabled -passwordNeverExpires $false -size
0 -ldap $ldap | where {($_.passwordAge.value -gt 0) -AND
($_.passwordAge.value.days -lt $maximumPasswordAge) -AND
(($maximumPasswordAge-$_.passwordAge.value.days) -le $expireIn)}
$minLogin = ([datetime]::Now).Date.AddDays(-60)
$newline = [char]13+[char]10
foreach ($user in $expiredUsers) {
if ($user.lastLogonTimestamp.value -eq $null) { continue }
if ($user.lastLogonTimestamp.value -lt $minLogin) { continue }
$remain = $maximumPasswordAge-($user.passwordAge.value.days)
if (($remain % 2) -ne 1) { continue }
$body="The password for your account '"+$user.samAccountName+"' will
expire in $remain days."+$newline+$newline+"To change your password,
YOUR INFORMATION HERE"
$body = $body + $newline+$newline + "YOUR INFORMATION HERE"
$body = $body + $newline+$newline + "YOUR INFORMATION HERE."
$subject="Your password will expire in $remain days"
$to = $user.email
Send-Mail -smtpServer "YOUR SMTP SERVER" -from "YOUR
ADMINISTRATOR ADDRESS" -to $to -subject $subject -body $body
}
--
mdbman66
Domain 'MaximumPasswordAge'password policy is not configured (set to
0).
At C:\scripts\exppwv3.ps1:16 char:6
+ throw <<<< "Domain 'MaximumPasswordAge'password policy is not
configured (set to 0)."
Any idea why I am getting this?
gb
--
hogcruzr05
--
mdbman66
Quest AD cmdlets has chnaged in v1.22 so chnage this line:
$maxPassAge = (Get-QADObject (Get-QADRootDSE).defaultNamingContextDN).MaximumPasswordAge.value.days
to
$maxPassAge = (Get-QADObject (Get-QADRootDSE).defaultNamingContextDN).MaximumPasswordAge.days
HTH
---
Shay Levy
Windows PowerShell MVP
http://blogs.microsoft.co.il/blogs/ScriptFanatic
PowerShell Toolbar: http://tinyurl.com/PSToolbar
h> Thanks for posting this. I am receiving an error tho'... Our domain
h> policy is set to 90 days, yet I receive-
h>
h> Domain 'MaximumPasswordAge'password policy is not configured (set to
h> 0).
h> At C:\scripts\exppwv3.ps1:16 char:6
h> + throw <<<< "Domain 'MaximumPasswordAge'password policy is not
h> configured (set to 0)."
h> Any idea why I am getting this? gb
h>
--
hogcruzr05
--
hogcruzr05
expiredUsers = Get-QADUser -enabled -passwordNeverExpires:$False `
-size 0 -ldap $ldap | `
Where-Object{ ($_.passwordAge.days -gt 0) `
-AND ($_.passwordAge.days -lt $maximumPasswordAge) `
-AND (($maximumPasswordAge-$_.passwordAge.days) -le $expireIn) }
It didn't get how you passed the value for passwordNeverExpires that's all.
Feel free to take out the ` above, I just don't like crowding things :)
Chris
Get-QADUser : Invalid filter format:
'userAccountControl:1.2.840.113556.1.4.803:=64)(mail=*'
At C:\scripts\plz.ps1:23 char:28
+ $expiredUsers = Get-QADUser <<<< -enabled
-PasswordNeverExpires:$false -size 0 -ldap $ldap | where
{($_.passwordAge.
days -gt 0) -AND ($_.passwordAge.days -lt $maximumPasswordAge) -AND
(($maximumPasswordAge-$_.passwordAge.days) -le $exp
ireIn)}
--
hogcruzr05
--
hogcruzr05
echo $ldap and tell us the contents of that at the moment? It looks like
a syntax error, nothing more, should be easy to fix (I hope).
Chris
--
hogcruzr05
Just for interest, it's dying with the query because it's not valid.
Changing it to this would allow it to use the query:
$ldap = "(&(!userAccountControl:1.2.840.113556.1.4.803:=64)(mail=*))"
But, that only allows it to use the query string, it doesn't make the
query exclude CannotChangePassword.
Doing that is a bit more complex... it can go something like this:
$expiredUsers = Get-QADUser -passwordNeverExpires:$False `
-Enabled -Size 0 | `
?{ ($_.passwordAge.days -gt 0) `
-AND $_.passwordAge.days -lt $maximumPasswordAge `
-AND ($maximumPasswordAge - $_.passwordAge.days) -le $expireIn `
-AND `
!(Get-QADPermission $_.DN -Deny -Rights "ExtendedRight" `
-ExtendedRight "User-Change-Password") }
It will slow down your query considerably (as well as popping up lots of
yellow warning messages which you can ignore).
Chris
$_.passwordAge.days -gt 0)-AND
($_.passwordAge.days -lt $maximumPasswordAge)
Because if you do that you can at least limit the check for
CannotChangePassword to accounts within the specified range rather than
everyone. Makes quite a difference to the speed of the script.
I suppose you might also pass the results of the first two checks into a
second Where-Object statement to check for whether they can or cannot
change the password. Just stikes me as rather messy (my opinion, of course).
Chris
--
hogcruzr05
That doesn't mean enumerating it is impossible (as I hoped to
demonstrate in my previous post), it just means that doing so is more
expensive as it requires a connection to each object.
If you have tens of thousands of objects to enumerate that cost may just
be too high. A few hundred, or even a few thousand, would still return
in a moderately reasonable time-frame.
This is the example using Where-Object twice:
$expiredUsers = Get-QADUser -passwordNeverExpires:$False `
-Enabled -Size 0 | `
?{ ($_.passwordAge.days -gt 0) `
-AND $_.passwordAge.days -lt $maximumPasswordAge `
-AND ($maximumPasswordAge - $_.passwordAge.days) -le $expireIn } | `
?{ !(Get-QADPermission $_.DN -Deny -Rights "ExtendedRight" `
-ExtendedRight "User-Change-Password") }
The first Where-Object drops all objects that don't meet the password
age criteria, only objects that pass the first are processed by the second.
I still feel it would be beneficial to deal with the PwdLastSet date
range within an LDAP filter rather than afterwards using Where-Object.
But I can appreciate that it's makes the code less easy to read :)
Do note that the check for PASSWD_CANT_CHANGE is not specific to any
language, it's a limitation within AD, you would see exactly the same
problem with VbScript. You can only work with what's there.
Chris
I was also looking for a script that can determine a user's password
expiry date and send email notifications when they are about to expire..
I now have a working code (at least on my side), and I am happy to share
it with everyone..
The code below is developed from Shay's code... (I modified the code in
the earlier posts and took out the complex stuff and also modified it to
meet my needs)... This is the first time I've touched powershell and
maybe my code is inefficient and/or is missing a lot of things, but at
least it works for me.. i've tried to put a lot of comments for people
who have just started powershell like me... Hope this helps (watch out
for line breaks):
# Start of script
# Check_password_expiry.ps1
# 16/07/2009
#
# Purpose:
# Powershell script to find out a list of users
# whose password is expiring within x number of days (as specified in
$days_before_expiry).
# Email notification will be sent to them reminding them that they need
to change their password.
#
# Requirements:
# password expiry.ps1 is dependant on Quest.ActiveRoles.ADManagement
snapin to get the AD attributes.
# The Quest.ActiveRoles.ADManagement snapin can be downloaded from
'PowerShell Commands (CMDLETs) for Active Directory by Quest Software'
(http://www.quest.com/powershell/activeroles-server.aspx)
# Look for ActiveRoles Management Shell for Active Directory (both
32-bit or 64-bit versions available)
# Also available in P:\Software\Microsoft\Windows Powershell snap-in.
#
# Note: This script needs to be run with administrator priviledges.
# If script is not run with administrator priviledges,
# Get-QADUser function will not return PasswordExpires and PwdLastSet
attributes correctly.
#
# You might need to sign the powershell script so that it can run in
batch mode (as opposed to interactive mode).
# To do that, you will need to run elevated command prompt, run
powershell (by typing powershell on the command prompt),
# next, type "set-executionpolicy RemoteSigned" (without the double
quotes) on the powershell prompt.
#####################
# Variables to change
#####################
# Days to Password Expiry
$days_before_expiry = 14
# SMTP Server to be used
$smtp = "INSERT YOUR SMTP MAIL SERVER HERE"
# "From" address of the email
$from = "INSERT THE FROM ADDRESS HERE"
# Administrator email
$admin = "INSERT ADMINISTRATOR ADDRESS HERE"
# Define font and font size
# ` or \ is an escape character in powershell
$font = "<font size=`"3`" face=`"Calibri`">"
##########################################
# Should require no change below this line
# (Except message body)
##########################################
function Send-Mail{
param($smtpServer,$from,$to,$subject,$body)
$smtp = new-object system.net.mail.smtpClient($SmtpServer)
$mail = new-object System.Net.Mail.MailMessage
$mail.from = $from
$mail.to.add($to)
$mail.subject = $subject
$mail.body = $body
# Send email in HTML format
$mail.IsBodyHtml = $true
$smtp.send($mail)
}
# Newline character
#$newline = [char]13+[char]10
$newline = "<br>"
# Get today's day, date and time
$today = (Get-date)
# Loads the Quest.ActiveRoles.ADManagement snapin required for the
script.
# (Will unload once powershell is exited)
add-pssnapin "Quest.ActiveRoles.ADManagement"
# Retrieves list of users whose account is enabled, has a password
expiry date and whose password expiry date within (is less than)
today+$days_before_expiry
$users_to_be_notified = Get-QADUser -Enabled
-passwordNeverExpires:$False | Where {($_.PasswordExpires -lt
$today.AddDays($days_before_expiry))}
# Send email to notify users
foreach ($user in $users_to_be_notified) {
# Calculate the remaining days
# If result is negative, then it means password has already expired.
# If result is positive, then it means password is expiring soon.
$days_remaining = ($user.PasswordExpires - $today).days
# Set font for HTML message
$body = $font
# For users whose password already expired
if ($days_remaining -le 0) {
# Make the days remaining positive (because we are reporting it as
expired)
$days_remaining = [math]::abs($days_remaining)
# Add it in a list (to be sent to admin)
$expired_users += $user.name + " - <font color=blue>" +
$user.LogonName + "</font>'s password has expired <font color=blue>" +
$days_remaining + "</font> day(s) ago." + $newline
# If there is an email attached to profile
if ($user.Email -ne $null) {
# Email notification to user
$to = $user.Email
$subject = "Reminder - Password has expired " + $days_remaining + "
day(s) ago."
# Message body is in HTML font
$body += "Dear " + $user.givenname + "," + $newline + $newline
$body += "This is a friendly reminder that your password for account
'<font color=blue>" + $user.LogonName + "</font>' has already expired "
+ $days_remaining + " day(s) ago."
$body += " Please contact the systems administrator to arrange for
your password to be reset."
}
else {
# Email notification to administrator
$to = $admin
$subject = "Reminder - " + $user.LogonName+ "'s Password has expired
" + $days_remaining + " day(s) ago."
# Message body is in HTML font
$body += "Dear administrator," + $newline + $newline
$body += "<font color=blue>" + $user.LogonName+ "</font>'s password
has expired <font color=blue>" + $days_remaining + " day(s)
ago</font>."
$body += " However, the system has detected that there is no email
address attached to the profile."
$body += " Therefore, no email notifications has been sent to " +
$user.Name + "."
$body += " Kindly reset the password and notify user of the password
change."
$body += " In addition, please add a corresponding email address to
the profile so emails can be sent directly for future notifications."
}
# Put a timestamp on the email
$body += $newline + $newline + $newline + $newline
$body += "<h5>Message generated on: " + $today + ".</h5>"
$body += "</font>"
# Invokes the Send-Mail function to send notification email
Send-Mail -smtpServer $smtp -from $from -to $to -subject $subject
-body $body
}
# For users whose password is expiring
# if ($days_remaining -gt 0) {
else {
# Add it in a list (to be sent to admin)
$expiring_users += $user.name + " - <font color=blue>" +
$user.LogonName + "</font> has <font color=blue>" + $days_remaining +
"</font> day(s) remaing left to change his/her password." + $newline
# If there is an email attached to profile
if ($user.Email -ne $null) {
# Email notification to user
$to = $user.Email
$subject = "Reminder - Password is expiring in " + $days_remaining +
" day(s)."
# Message body is in HTML font
$body += "Dear " + $user.givenname + "," + $newline + $newline
$body += "This is a friendly reminder that your password for account
'<font color=blue>" + $user.LogonName + "</font>' is due to expire in "
+ $days_remaining + " day(s)."
$body += " Please remember to change your password before <font
color=blue>" + $user.PasswordExpires.date.tostring('dd/MM/yyyy') +
"</font>."
}
else {
# Email notification to administrator
$to = $admin
$subject = "Reminder - " + $user.LogonName+ "'s Password is expiring
in " + $days_remaining + " day(s)."
# Message body is in HTML font
$body += "Dear administrator," + $newline + $newline
$body += "<font color=blue>" + $user.LogonName+ "</font>'s password
is expiring in <font color=blue>" + $days_remaining + " day(s)</font>."
$body += " However, the system has detected that there is no email
address attached to the profile."
$body += " Therefore, no email notifications has been sent to " +
$user.Name + "."
$body += " Kindly remind him/her to change the password before <font
color=blue>" + $user.PasswordExpires.date.tostring('dd/MM/yyyy') +
"</font>."
$body += " In addition, please add a corresponding email address to
the profile so emails can be sent directly for future notifications."
}
# Put a timestamp on the email
$body += $newline + $newline + $newline + $newline
$body += "<h5>Message generated on: " + $today + ".</h5>"
$body += "</font>"
# Invokes the Send-Mail function to send notification email
Send-Mail -smtpServer $smtp -from $from -to $to -subject $subject
-body $body
}
}
# If there are users with expired password or users whose password is
expiring soon
if ($expired_users -ne $null -and $expiring_users -ne $null) {
# Email notification to administrator
$to = $admin
$subject = "Reminder - Password reminder for domain users."
# Message body is in HTML font
$body = $font
$body += "Dear administrator," + $newline + $newline
$body += "Please note that the users below have needs to change their
password soon." + $newline + $newline + $newline
$body += "<b>Users with expired password:</b>" + $newline
$body += $expired_users + $newline + $newline
$body += "<b>Users with password expiring soon:</b>" + $newline
$body += $expiring_users
# Put a timestamp on the email
$body += $newline + $newline + $newline + $newline
$body += "<h5>Message generated on: " + $today + ".</h5>"
$body += "</font>"
# Invokes the Send-Mail function to send notification email
Send-Mail -smtpServer $smtp -from $from -to $to -subject $subject
-body $body
}
# End of script
To invoke the script, open a command prompt (with elevation), navigate
to where the script is, and type "powershell Check_password_expiry.ps1"
(if the filename is Check_password_expiry.ps1).
Btw, I've only tested the script on my operating system which is a
32bit Windows 2008 Standard Edition and it is a domain controller. But
hopefully it will work for you too.
--
jemzzz
--
hogcruzr05
I hope the script works for you :) It does not have the fancy ldap
thing that Shay and Chris mentioned, but hey, it works for me :P
If it doesn't work, I suggest that you debug the variables line by line
and pay particular attention to $users_to_be_notified.. I spent a lot of
time on that because I kept getting no results for that, although I
already had the correct logic (it turns out that I needed to run the
whole thing in elevated command prompt).. so, try to play around with it
(execute the Get-QADUser queries step by step to see where the error
is).. And if you are still stuck or you can't get the script to work, do
let me know.. I'm not an expert in this, but I will try my best to help
you out :)
With regards to the font size.. I'm also no HTML expert, but I think
HTML fonts are a little different than normal fonts... Initially I did
set the font to 10 (which I normally use), but I found out in the mail
that it is too big.. After playing around with it, I sticked to 3
because I think it displays the same size as normal font size 10 on my
screen... you can try playing around with it, it will only change the
display if you change the value (does not have any functional effect on
processing the send mail procedure) ;)
Well, I hope this helps and I also hope that the script works for you,
best of luck to you hogcruzr05 ;)
--
jemzzz
$ReqVersion = [version]"1.2.2.1254"
$QadVersion = (Get-PSSnapin Quest.ActiveRoles.ADManagement).Version
if($QadVersion -lt $ReqVersion)
{
throw "Quest AD cmdlets version $ReqVersion is required. Please download
the latest version"
}
function Send-Mail
{
param($smtpServer,$from,$to,$subject,$body)
$smtp = new-object system.net.mail.smtpClient($SmtpServer)
$mail = new-object System.Net.Mail.MailMessage
$mail.from = $from
$mail.to.add($to)
$mail.subject = $subject
$mail.body = $body
$smtp.send($mail)
}
$maxPassAge = (Get-QADObject (Get-QADRootDSE).defaultNamingContextDN).MaximumPasswordAge.days
if($maxPassAge -le 0)
{
throw "Domain 'MaximumPasswordAge' password policy is not configured."
}
$old = 14
$mailForm = "y...@domain.com"
$PSEmailServer = "exServerName"
Get-QADUser -Enabled -PasswordNeverExpires:$false -SizeLimit 0 -Email * | `
Select-Object Name,email,@{n="Expires";e={ $maxPassAge-$_.passwordAge.days
}} | `
Where-Object {$_.Expires -gt 0 -AND $_.Expires -ge $old} | Foreach-Object {
$subject="Your password will expire in $expireIn days"
Send-Mail -smtpServer $PSEmailServer -From $mailForm -To $_.email -subject
$subject -body $subject
}
---
Shay Levy
Windows PowerShell MVP
http://blogs.microsoft.co.il/blogs/ScriptFanatic
PowerShell Toolbar: http://tinyurl.com/PSToolbar
j> Hi hogcruzr05,
j>
j> I hope the script works for you :) It does not have the fancy ldap
j> thing that Shay and Chris mentioned, but hey, it works for me :P
j>
j> If it doesn't work, I suggest that you debug the variables line by
j> line and pay particular attention to $users_to_be_notified.. I spent
j> a lot of time on that because I kept getting no results for that,
j> although I already had the correct logic (it turns out that I needed
j> to run the whole thing in elevated command prompt).. so, try to play
j> around with it (execute the Get-QADUser queries step by step to see
j> where the error is).. And if you are still stuck or you can't get the
j> script to work, do let me know.. I'm not an expert in this, but I
j> will try my best to help you out :)
j>
j> With regards to the font size.. I'm also no HTML expert, but I think
j> HTML fonts are a little different than normal fonts... Initially I
j> did set the font to 10 (which I normally use), but I found out in the
j> mail that it is too big.. After playing around with it, I sticked to
j> 3 because I think it displays the same size as normal font size 10 on
j> my screen... you can try playing around with it, it will only change
j> the display if you change the value (does not have any functional
j> effect on processing the send mail procedure) ;)
j>
j> Well, I hope this helps and I also hope that the script works for
j> you, best of luck to you hogcruzr05 ;)
j>
I've tried running your script on my 2008 DC but had no luck with it and get
the following error message when trying to invoke the script;
PS C:\scripts> C:\Scripts\Check_password_expiry.ps1
The operation '[$null] - [System.DateTime]' is not defined.
At C:\Scripts\Check_password_expiry.ps1:82 char:43
+ $days_remaining = ($user.PasswordExpires - <<<< $today).days
Exception calling "Send" with "1" argument(s): "Failure sending mail."
At C:\Scripts\Check_password_expiry.ps1:60 char:11
+ $smtp.send( <<<< $mail)
Exception calling "Send" with "1" argument(s): "Failure sending mail."
At C:\Scripts\Check_password_expiry.ps1:60 char:11
+ $smtp.send( <<<< $mail)
before i invoked it, i installed powershell and the
Quest.ActiveRoles.ADManagement Snapin version 1.2.2 and i set the execution
policy to unrestricted. I modified the values for smtp server, e-mail address
for administrator and the from address. I invoked the above script with
elevated privileges.
Any suggestions?
Thanks.
I've published the latest version of the script, see if it helps:
http://blogs.microsoft.co.il/blogs/scriptfanatic/archive/2009/08/02/sending-expiry-password-notifications-to-users.aspx
---
Shay Levy
Windows PowerShell MVP
http://blogs.microsoft.co.il/blogs/ScriptFanatic
PowerShell Toolbar: http://tinyurl.com/PSToolbar
z> Hi Jemzz,
z>
z> I've tried running your script on my 2008 DC but had no luck with it
z> and get the following error message when trying to invoke the script;
z>
z> PS C:\scripts> C:\Scripts\Check_password_expiry.ps1
z> The operation '[$null] - [System.DateTime]' is not defined.
z> At C:\Scripts\Check_password_expiry.ps1:82 char:43
z> + $days_remaining = ($user.PasswordExpires - <<<< $today).days
z> Exception calling "Send" with "1" argument(s): "Failure sending
z> mail."
z> At C:\Scripts\Check_password_expiry.ps1:60 char:11
z> + $smtp.send( <<<< $mail)
z> Exception calling "Send" with "1" argument(s): "Failure sending
z> mail."
z> At C:\Scripts\Check_password_expiry.ps1:60 char:11
z> + $smtp.send( <<<< $mail)
z> before i invoked it, i installed powershell and the
z> Quest.ActiveRoles.ADManagement Snapin version 1.2.2 and i set the
z> execution policy to unrestricted. I modified the values for smtp
z> server, e-mail address for administrator and the from address. I
z> invoked the above script with elevated privileges.
z>
z> Any suggestions?
z>
z> Thanks.
z>
z> "jemzzz" wrote:
z>
--
hogcruzr05
hogcruzr05 wrote:
Re: script to determine password expire date and send email notificati
13-Agu-09
--
hogcruzr05
EggHeadCafe - Software Developer Portal of Choice
BOOK REVIEW: Professional Silverlight 2 for ASP.NET Developers [WROX]
http://www.eggheadcafe.com/tutorials/aspnet/3573070a-b0b0-49e5-99c8-cddc274ec7f8/book-revew-professional.aspx