Windows IT Pro is the leading independent community for IT professionals deploying Microsoft Windows server and client applications and technologies.
  
  
  Advanced Search 


Return to article

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?

gbecerra@hotmail.com.ar -May 20, 2009
Windows IT Pro Home Register FAQ for Windows WinInfo News
Europe Edition About Us Contact Us/Customer Service Media Kit Affiliates / Licensing  
SQL Server Magazine Office & SharePoint Pro DevProConnections IT Job Hound
Left-Brain.com Technology Resource Directory asp.netPRO ITTV Windows SuperSite 
 
 Windows IT Pro is a Division of Penton Media Inc.
 © 2009 Penton Media, Inc. Terms of Use | Privacy Statement