How can I use a script to determine password-expiration dates for users in a domain or an organizational unit (OU) and send an email message to accounts whose passwords expire soon?
A.
I recently had a client whose users had Active Directory (AD) accounts only for Microsoft Outlook Web Access (OWA); users would never actually log on by using the AD account. Although OWA notifies users that their passwords expire soon, the company wants users to receive an email message letting them know that the account is due to expire.
I wrote the following script, reportpasswordchange.vbs, which you can download at http://www.windowsitpro.com/articles/download/rptpaswdchange.zip, that searches every container and OU for users in the savilltech.com domain and for the number of days that passwords last (your maximum password age). You'll need to enter the root container to search (e.g., dc=savilltech,dc=com), and you'll need to change the sender address and possibly also the subject and message text in the script's SendEmailMessage subroutine. The script expects to run on a server running Microsoft IIS and SMTP, but you can modify this. Doing so would require rewriting parts of the script and is beyond the scope of this Q&A. Save the script as reportpasswordchange.vbs and execute it via CScript. (Because of space limitations, some lines in the script wrap to two lines, so to avoid runtime errors, run the downloaded version.)
'
' John Savill 8th June 2005
' Runs check on last password change date.
'
Option Explicit
Dim objCommand, objConnection, objChild, objUserConnection, strBase, _
strFilter, strAttributes, strPasswordChangeDate, intPassAge
Dim lngTZBias, objPwdLastSet, strEmailAddress, objMessage
Dim objShell, lngBiasKey, k, PasswordExpiry, strRootDomain
Dim strQuery, objRecordset, strName, strCN
' ********** CHANGE THESE VALUES TO PASSWORD EXPIRY AND ROOT OF
' DIRECTORY WHERE USERS WILL BE SEARCHED ***********
PasswordExpiry=35
strRootDomain="dc=savilltech,dc=com"
' **************************************************************
' **************************************************************
' Obtain local Time Zone bias from machine registry.
Set objShell = CreateObject("Wscript.Shell")
lngBiasKey = _
objShell.RegRead("HKLM\System\CurrentControlSet\Control\" & _
"TimeZoneInformation\ActiveTimeBias")
If UCase(TypeName(lngBiasKey)) = "LONG" Then
lngTZBias = lngBiasKey
ElseIf UCase(TypeName(lngBiasKey)) = "VARIANT()" Then
lngTZBias = 0
For k = 0 To UBound(lngBiasKey)
lngTZBias = lngTZBias + (lngBiasKey(k) * 256^k)
Next
End If
Set objCommand = CreateObject("ADODB.Command")
Set objConnection = CreateObject("ADODB.Connection")
objConnection.Provider = "ADsDSOObject"
objConnection.Open "Active Directory Provider"
objCommand.ActiveConnection = objConnection
strBase = ""
strFilter = "(&(objectCategory=person)(objectClass=user))"
strAttributes = "sAMAccountName,cn,mail,pwdLastSet,distinguishedName"
strQuery = strBase & ";" & strFilter & ";" & strAttributes & ";subtree"
objCommand.CommandText = strQuery
objCommand.Properties("Page Size") = 100
objCommand.Properties("Timeout") = 30
objCommand.Properties("Cache Results") = False
Set objRecordSet = objCommand.Execute
' WScript.echo "Running at " & Date()
Do Until objRecordSet.EOF
strName = objRecordSet.Fields("sAMAccountName").Value
strCN = objRecordSet.Fields("cn").value
strEmailAddress = objRecordSet.Fields("mail").value
Wscript.Echo "NT Name: " & strName & ", Common Name: " & strCN
Set objUserConnection = GetObject("LDAP://" & _
objRecordSet.Fields("distinguishedName").Value)
Set objPwdLastSet = objUserConnection.pwdLastSet
strPasswordChangeDate = Integer8Date(objPwdLastSet, lngTZBias)
WScript.Echo vbTab & "Password last changed at " & _
strPasswordChangeDate
intPassAge = DateDiff("d", strPasswordChangeDate, Now)
WScript.Echo vbTab & "Password changed " & intPassAge & " days ago"
If intPassAge = (PasswordExpiry-3) Then
WScript.echo vbTab & "Sending user notification to " & _
strEmailAddress & " that password expires in 3 days"
Call SendEmailMessage(strEmailAddress, 3)
ElseIf intPassAge = (PasswordExpiry-6) Then
WScript.echo vbTab & "Sending user notification to " & _
strEmailAddress & " that password expires in 6 days"
Call SendEmailMessage(strEmailAddress, 6)
ElseIf intPassAge = (PasswordExpiry-9) Then
WScript.echo vbTab & "Sending user notification to " & _
strEmailAddress & " that password expires in 9 days"
Call SendEmailMessage(strEmailAddress, 9)
End If
objRecordSet.MoveNext
Loop
objConnection.Close
Function Integer8Date(objDate, lngBias)
' Function to convert Integer8 (64-bit) value to a date, adjusted for
' local time zone bias.
Dim lngAdjust, lngDate, lngHigh, lngLow
lngAdjust = lngBias
lngHigh = objDate.HighPart
lngLow = objdate.LowPart
' Account for the error in IADslargeInteger property methods.
If lngLow < 0 Then
lngHigh = lngHigh + 1
End If
If (lngHigh = 0) And (lngLow = 0) Then
lngAdjust = 0
End If
lngDate = #1/1/1601# + (((lngHigh * (2 ^ 32)) _
+ lngLow) / 600000000 - lngAdjust) / 1440
' Trap error if lngDate is overly large.
On Error Resume Next
Integer8Date = CDate(lngDate)
If Err.Number < > 0 Then
On Error GoTo 0
Integer8Date = #1/1/1601#
End If
On Error GoTo 0
End Function
Sub SendEmailMessage(strDestEmail, strNoOfDays)
Set objMessage = CreateObject("CDO.Message")
objMessage.Subject = "Password Expires in " & strNoOfDays & " days"
objMessage.Sender = "administrator@savilltech.com"
objMessage.To = strDestEmail
objMessage.TextBody = "Your password expires in " & strNoOfDays & " _
days. Please goto http://changepass.com and reset" objMessage.Send
End Sub
To execute the script, run the command
reportpasswordchange.vbs
You'll see output on screen similar to this:
Microsoft (R) Windows Script Host Version 5.6
Copyright (C) Microsoft Corporation 1996-2001. All rights reserved.
NT Name: bwayne, Common Name: Bruce Wayne
Password last changed at 5/7/2005 8:59:01 PM
Password changed 39 days ago
NT Name: CKent, Common Name: Clark Kent
Password last changed at 5/7/2005 9:01:32 PM
Password changed 39 days ago
NT Name: wwest, Common Name: Wally West
Password last changed at 6/8/2005 7:55:47 PM
Password changed 7 days ago
NT Name: krayner, Common Name: Kyle Rayner
Password last changed at 5/7/2005 9:19:52 PM
Password changed 39 days ago
Reader Comments
Great script!
Be careful with one thing. Users with non-expiring passwords will also get the warning emails. You can change the strFilter to not include these users:
strFilter = "(&(objectCategory=person)(objectClass=user)(!userAccountControl:1.2.840.113556.1.4.803:=65536))"
Anonymous User -June 24, 2005
I had problems with this. The functions wouldn't work
Anonymous User -June 28, 2005
This works great. Regarding the first comment, I
also noticed that it checks on disabled accounts.
So I set the strFilter = "(&(objectCategory=person)
(objectClass=user) (!userAccountControl:1.2.840.113556.1.4.803:=2)
(!userAccountControl:1.2.840.113556.1.4.803:=65536))"
Now it won't check disabled accounts or accounts
whose passwords should never expire.
Also, I had to add objMessage.From under
objMessage.Sender (which probably is not needed) so
Exchange would understand who it was from.
Anywho, great script!
Anonymous User -July 26, 2005
thank you
Anonymous User -July 27, 2005
really great script.
Can someone put final code with some grooming?
Thanks
Anonymous User -July 28, 2005
This script looks like exactly what I need - most of my users access email via IMAP - they won't recieve any notification that their password has expired!
However, on running the script I get the following error message:
reportpasswordchange.vbs(46,1) Provider: Table does not exist.
I have changed the strRootDomain as appropriate.
Is this a genuine error message, or is this the message I would get if no users passwords were to expire in the next 9 days?
Anonymous User -August 18, 2005
Oops - my problem was that I left the second part of the dc as 'dc=com', whereas it should be 'dc=local' for my system. Is working fine now, but still need to filter out the non-expiring accounts
Anonymous User -August 18, 2005
Works well but how come it does not send any email out. Is there a change somewhere that's required?
Anonymous User -August 26, 2005
Dudes you all Rock!
Awesome Script and Thanks to those who replied with the filter updates.
Anonymous User -August 30, 2005
What should the PasswordExpiry value be set to?
Anonymous User -September 09, 2005
I am getting an error about line 139 objMessage.To = strDestEmail
the error is a type mimatch
please help
heleyj@aol.com -September 29, 2005
Some minor change:
intPassAge = (PasswordExpiry-35) 'to get difference and put it in to email, 35 can be
If intPassAge =<20 Then 'to check the difference with your domain gpo, like 20 can be the expiration period you use in your domain gpo
WScript.echo vbTab & "Sending user notification to " & strEmailAddress & " that password expires in " & intPassAge & " days"
Call SendEmailMessage(strEmailAddress, intPassAge)
End If
Nikolai -January 12, 2006
excellent script; I am not much of a scripter - can the filter be modified to only run against "ou=Users" ? I would like to start at the top of the tree and run down, only effecting the ou=Users ou's
darrylkf -January 19, 2006
This tool does it!! I found a sweet new tool called Password Reminder PRO from SysOp Tools (http://www.sysoptools.com) that sends active directory password expire notification to users via email, works with Exchange or any other mail server. There's also a reporting console that shows status of all AD user acounts and it emails you a daily report summary of users that received a password expiration email alert reminder for that day. It also knows which accounts not to send an email to (non-expiring acounts).
Anywho, it's a free beta download on their site and I haven't seen anything else like it. Sure beats messing with the scripting.... If anyone is looking to do this definitely try it out / install is plain easy. IMO It's a really cool utility and is working great for us so far with supporting our 80+ email-only (OWA) remote users. No one is calling our help desk anymore complaining of expired password :)
klewis -December 14, 2006
p.s. to my prev post- the beta key for the Password Reminder PRO software has no user count limit and is good for 90 days / no odd catches or nagware, etc - it's all pretty straight-forward (how it should be :)).
klewis -December 14, 2006
I'll add that I think this only works if you are running a local SMTP service. If you aren't there's no way this script knows where to send the email.
In order to send the email directly to a remote SMTP server add these 3 lines directly after you Create the CDO.Message object:
objMessage.Configuration.Fields.Item("http://schemas.microsoft.com/cdo/configuration/smtpserver") = SMTPServer ' "smtp.domain.com"
objMessage.Configuration.Fields.Item("http://schemas.microsoft.com/cdo/configuration/sendusing") = 2
objMessage.Configuration.Fields.Update
bgh0055 -May 09, 2008
This script works fine for all users in my environment except from few users which has a "/" in their display name.
Would truly appreciate if you can shed some light to help me fix this.
EG:
Display Name= FirstName LastName /ComName
syunus -August 22, 2008
Hello,
I use this script but i have a problem, i see all users and not only the password expire in 35 days. Can help me please?
Free CDs Offer Fundamental Content for IT Pros Are you up to speed on the latest technologies and solutions? Don't miss out on your chance to get up to speed quickly on fundamental, in-depth information on some of the hottest topics in our library of content.
Let Your Users Reset Their Own Passwords: Free Download Try a 30 day free trial of Desktop Authority Password Self-Service – it provides an easy-to-use, robust system for allowing users to reset their own forgotten passwords or locked accounts.
Get Windows IT Pro & Mark Minasi’s Favorite Power Tools Guide Order Windows IT Pro now and get "More of Mark Minasi's Favorite Power Tools"--a in-depth guide to the most useful Windows commands --FREE with your paid order! Subscribe today, and save 58% off the cover price!
Deep Dive into VMware vSphere, eLearning Series Join John Savill to explore the major functionality capabilities of the vSphere virtualization platform, including identification of the changes from ESX 3.5.