Tom wrote:
> We host email for about a dozen remote locations. Since these users never
> actually log into our network except for email (RPC over HTTPS & OWA),
> they are never notified that their password is set to expire.
>
> Is there a query that can be run on an OU that gathers the password
> expiration dates for users? I can then take that data and script email
> notifications.
>
I have an example VBScript program that determines when the password was
last set for all users in the domain linked here:
http://www.rlmueller.net/PwdLastChanged.htm
This program can be modified to only deal with users in a specified OU. It
can also be revised to calculate when the password for each user will
expire, assuming a specified domain maximum password age policy. For
example:
============
' VBScript program to determine when passwords expire
' for users in a specified OU.
Option Explicit
Const ADS_UF_PASSWD_CANT_CHANGE = &H40
Const ADS_UF_DONT_EXPIRE_PASSWD = &H10000
Dim strFilePath, objFSO, objFile, adoConnection, adoCommand
Dim strFilter, strQuery, adoRecordset
Dim strDN, objShell, lngBiasKey, lngBias, blnPwdExpire
Dim objDate, dtmPwdLastSet, lngFlag, k
Dim lngMaxPwdAge, dtmPwdExpires
' Specify maximum password age policy in days.
lngMaxPwdAge = 42
' Check for required arguments.
If (Wscript.Arguments.Count < 1) Then
Wscript.Echo "Arguments <FileName> required. For example:" & vbCrLf _
& "cscript PwdLastChanged.vbs c:\MyFolder\UserList.txt"
Wscript.Quit(0)
End If
strFilePath = Wscript.Arguments(0)
Set objFSO = CreateObject("Scripting.FileSystemObject")
' Open the file for write access.
On Error Resume Next
Set objFile = objFSO.OpenTextFile(strFilePath, 2, True, 0)
If (Err.Number <> 0) Then
On Error GoTo 0
Wscript.Echo "File " & strFilePath & " cannot be opened"
Set objFSO = Nothing
Wscript.Quit(1)
End If
On Error GoTo 0
' Obtain local time zone bias from machine registry.
Set objShell = CreateObject("Wscript.Shell")
lngBiasKey = objShell.RegRead("HKLM\System\CurrentControlSet\Co ntrol\" _
& "TimeZoneInformation\ActiveTimeBias")
If (UCase(TypeName(lngBiasKey)) = "LONG") Then
lngBias = lngBiasKey
ElseIf (UCase(TypeName(lngBiasKey)) = "VARIANT()") Then
lngBias = 0
For k = 0 To UBound(lngBiasKey)
lngBias = lngBias + (lngBiasKey(k) * 256^k)
Next
End If
' Use ADO to search the domain for all users.
Set adoConnection = CreateObject("ADODB.Connection")
Set adoCommand = CreateObject("ADODB.Command")
adoConnection.Provider = "ADsDSOOBject"
adoConnection.Open "Active Directory Provider"
Set adoCommand.ActiveConnection = adoConnection
' Filter to retrieve all user objects.
strFilter = "(&(objectCategory=person)(objectClass=user))"
' Specify DN of OU as base of query.
strQuery = "<LDAP://ou=West,dc=MyDomain,dc=com>;" & strFilter _
& ";distinguishedName,pwdLastSet,userAccountControl; subtree"
adoCommand.CommandText = strQuery
adoCommand.Properties("Page Size") = 100
adoCommand.Properties("Timeout") = 30
adoCommand.Properties("Cache Results") = False
' Enumerate all users. Write each user's Distinguished Name,
' whether they are allowed to change their password, and when
' they last changed their password to the file.
Set adoRecordset = adoCommand.Execute
Do Until adoRecordset.EOF
strDN = adoRecordset.Fields("distinguishedName").Value
lngFlag = adoRecordset.Fields("userAccountControl").Value
blnPwdExpire = True
If ((lngFlag And ADS_UF_PASSWD_CANT_CHANGE) <> 0) Then
blnPwdExpire = False
End If
If ((lngFlag And ADS_UF_DONT_EXPIRE_PASSWD) <> 0) Then
blnPwdExpire = False
End If
Set objDate = adoRecordset.Fields("pwdLastSet").Value
dtmPwdLastSet = Integer8Date(objDate, lngBias)
If (blnPwdExpire = True) Then
dtmPwdExpires = DateAdd("d", lngMaxPwdAge, dtmPwdLastSet)
objFile.WriteLine strDN & ";" & blnPwdExpire _
& ";" & dtmPwdLastSet & ";" & dtmPwdExpires
Else
objFile.WriteLine strDN & ";" & blnPwdExpire _
& ";" & dtmPwdLastSet & ";" & "<Never>"
End If
adoRecordset.MoveNext
Loop
adoRecordset.Close
' Clean up.
objFile.Close
adoConnection.Close
Wscript.Echo "Done"
Function Integer8Date(ByVal objDate, ByVal 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 bug 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
Integer8Date = CDate(lngDate)
End Function
--
Richard Mueller
MVP Directory Services
Hilltop Lab -
http://www.rlmueller.net
--