How to: Search for the Password Change History of Users

The password change history is stored in three Windows Management Instrumentation (WMI) classes: MIIS_PasswordChangeHistorySource Class, MIIS_PasswordChangeHistoryTarget Class, and MIIS_PasswordChangeQueue Class.

The following Microsoft Visual Basic Scripting Edition (VBScript) example searches for the password change history for a user. To use the classes in this script, you must be logged on as a member of the MIISAdmin or MIISBrowse security group.

Option Explicit

On Error resume next

Dim MIIS_AccountName
Dim MIIS_Password
Dim MIIS_MachineName
Dim Service
Dim objLocator 
Dim WMIQuery
Dim CsObjects
Dim CsObject
Dim CsObjCount
Dim SearchDomain
Dim SearchUser
Dim UserCsGuid
Dim Changes
Dim DetailCount
Dim PasswordChange
Dim BufferLen
Dim MAGuid
Dim MAName
Dim Targets
Dim Target
Dim TargetCount
Dim NumTargets
Dim QueueCount
Dim QueueEntry
Dim PasswordQueue
Dim NumQueue
Dim MVQuery
Dim ConnectorsCount
Dim RelatedCSObjects
Dim ConnectedCSObjects

Const MIIS_WMI_Namespace = "root\MicrosoftIdentityIntegrationServer"
Const PktPrivacy = 6
Const wbemAuthenticationLevelPkt= 6
Const strPad  = "                                :"
Const strLine = "---------------------------------"
Const strQLine = " --------------------------------"
Const strTLine = "  -------------------------------"

MAGuid = array()
MAName = array()

BufferLen = Len(strPad)

' Check that you have the correct number of options.
If WScript.Arguments.Count <> 2 and WScript.Arguments.Count <> 5 Then
  If InStr(1,Wscript.Arguments.Item(0),"?",vbTextCompare) = 0 Then WScript.Echo _
    "Invalid arguments detected" & vbcrlf
  Usage
  WScript.Quit(1)
End If

SearchDomain = WScript.Arguments.Item(0)
SearchUser   = WScript.Arguments.Item(1)

If WScript.Arguments.Count = 5 Then
  MIIS_AccountName = WScript.Arguments.Item(2)
  MIIS_Password    = WScript.Arguments.Item(3)
  MIIS_MachineName = WScript.Arguments.Item(4)
End If

If Len(MIIS_AccountName) = 0 Then
  Set Service = GetObject("WinMgmts:{authenticationLevel=PktPrivacy}!" & MIIS_WMI_Namespace)
Else
  Set objLocator = CreateObject("WbemScripting.SWbemLocator")
  objLocator.Security_.AuthenticationLevel = wbemAuthenticationLevelPkt
  Set Service = objLocator.ConnectServer(MIIS_MachineName , MIIS_WMI_Namespace,  _
    MIIS_AccountName, MIIS_Password)
End If

If Err.Number <> 0 Then ErrorHandler("ERROR: " & Err.Description)

FindCSObject SearchDomain, SearchUser
CsObjCount = CsObjects.Count

If CsObjCount = 0 and Err.Number = 0 Then ErrorHandler(vbCrLf & _
  "Unable to locate " & SearchDomain & "\" & SearchUser & ".")
If CsObjCount = 0 and Err.Number <> 0 Then ErrorHandler(vbCrLf & _
  "ACTION: Verify that your user account is in the MIIS_Browse NT Security Group."_
   & vbCrLf & vbCrLf & "Error Number     : " & Err.Number & vbCrLf & "Error Description: "_
   & Err.Description )

If IsNull(CSObject.MvGuid) Then 
  ErrorHandler(vbCrLf & "The connector space object is a disconnector.")
  WScript.Quit(0)
End If

UserCsGuid = CsObject.Guid
WMIQuery = "SELECT * FROM MIIS_PasswordChangeHistorySource WHERE " & _ 
  "CsGuid = '" & UserCsGuid & "'"
Set Changes = Service.ExecQuery (WMIQuery)

Wscript.Echo ""
WScript.Echo FormatOutput("Account") & CsObject.Account
WScript.Echo FormatOutput("Domain")  & CsObject.Domain
WScript.Echo FormatOutput("MA Name") & CsObject.MaName
WScript.Echo FormatOutput("MA Partition") & CsObject.PartitionName
WScript.Echo FormatOutput("MA Guid") & CsObject.MaGuid
WScript.Echo FormatOutput("CS Guid") & UserCsGuid
WScript.Echo FormatOutput("Password History Source Entries") & Changes.Count

MVQuery = "Select * from MIIS_CSObject where mvguid='" & CSObject.MVGUID & "'"

Set RelatedCSObjects = Service.ExecQuery(MVQuery)
WScript.Echo FormatOutput("Number of Connectors") & RelatedCSObjects.Count
ConnectorsCount = 0

Wscript.echo vbcrlf & strLine
Wscript.echo FormatOutput("WMI Password Change/Set History")
Wscript.echo strLine

For Each ConnectedCSObjects in RelatedCsObjects
  ConnectorsCount = ConnectorsCount + 1
  DisplayConnectorValues
Next
WScript.Echo ""

DetailCount = 0

For Each PasswordChange in Changes
  DetailCount = DetailCount +1
  QueueCount = 0
  TargetCount = 0

WScript.Echo vbcrlf & strLine
WScript.Echo FormatOutput(" Password History Source Entry " & DetailCount) 
WScript.Echo strLine & vbcrlf
  DisplayPwdSrcValues

WMIQuery = "Select * from MIIS_PasswordChangeQueue WHERE " & _
  "ReferenceGuid = '" & PasswordChange.ReferenceGuid & "'"
Set PasswordQueue = Service.ExecQuery (WMIQuery)

WMIQuery = "Select * from MIIS_PasswordChangeHistoryTarget WHERE " & _
  "ReferenceGuid = '" & PasswordChange.ReferenceGuid & "'"
Set Targets = Service.ExecQuery (WMIQuery)

PrintQueueTitle

NumQueue = PasswordQueue.count
NumTargets = Targets.count
WScript.Echo FormatOutput(" Password Target Entries") & NumTargets
WScript.Echo

For Each Target in Targets
  TargetCount=TargetCount+1
  DisplayPwdTargetValues 
Next

For Each QueueEntry in PasswordQueue
  QueueCount = QueueCount + 1
  Wscript.echo vbcrlf & strLine
  Wscript.echo FormatOutput(" Password Queue Entries")
  Wscript.echo strLine
  DisplayPwdQueueValues
Next

Next

Sub PrintQueueTitle
  WScript.Echo FormatOutput(" Password Queue Entries") & PasswordQueue.Count
End Sub

Sub FindCSObject(DomainName, UserName)
  On Error Resume Next

  WMIQuery = "SELECT * FROM MIIS_CSObject WHERE DOMAIN='" & DomainName & _
    "' and ACCOUNT='" & UserName & "'"

  Set CsObjects = Service.ExecQuery(WMIQuery)

  'Move to the first object in the CsObjects Collection and Exit
  For Each CsObject in CsObjects
    Exit For
  Next
End Sub

Sub DisplayPwdSrcValues
  WScript.Echo FormatOutput(" DN") & PasswordChange.DN
  WScript.Echo FormatOutput(" Source Server") & PasswordChange.SourceServer
  WScript.Echo FormatOutput(" Source Change Time") & PasswordChange.SourceChangeTime
  WScript.Echo FormatOutput(" <tla rid="fim_sync_short"/> Receive Time") & PasswordChange.MIISReceiveTime
  WScript.Echo FormatOutput(" Tracking Guid") & PasswordChange.GUID
  WScript.Echo FormatOutput(" Reference Guid") & PasswordChange.ReferenceGuid
  WScript.Echo FormatOutput(" MA Name") & GetMANameFromGuid(PasswordChange.MAGUID)
  WScript.Echo FormatOutput(" MA Guid") & PasswordChange.MAGUID
  WScript.Echo FormatOutput(" CS Guid") & PasswordChange.CsGUID
  WScript.Echo FormatOutput(" Return Code") & PasswordChange.ReturnCode

End Sub

Sub DisplayPwdTargetValues
  Wscript.Echo strTLine
  WScript.Echo FormatOutput("  : Target Entry " & TargetCount & " of " & NumTargets)
  Wscript.Echo strTLine
  WScript.Echo FormatOutput("    DN") & Target.DN
  WScript.Echo FormatOutput("    Target Guid") & Target.Guid
  Wscript.Echo FormatOutput("    Reference Guid") & Target.ReferenceGuid
  WScript.Echo FormatOutput("    Retry Count") & Target.RetryCount
  WScript.Echo FormatOutput("    MA Name") & GetMANameFromGuid(Target.MAGUID)
  WScript.Echo FormatOutput("    MA Guid") & Target.MAGUID
  WScript.Echo FormatOutput("    CS Guid") & Target.CsGUID
  WScript.Echo FormatOutput("    RetryLimit") & Target.ReachedRetryLimit
  WScript.Echo FormatOutput("    <tla rid="fim_sync_short"/> Time Received") & Target.MIISReceiveTime
  Wscript.Echo FormatOutput("    Last Attempt Time") & Target.LastAttemptTime
  WScript.Echo FormatOutput("    Final Return Code") & Target.FinalReturnCode
  WScript.Echo FormatOutput("    Attempt Details") & vbCrLf & Target.AttemptDetails
  
  WScript.Echo
End Sub

Sub DisplayPwdQueueValues
  If QueueCount > 1 Then Wscript.Echo strTLine
  WScript.Echo FormatOutput("  : Queue Entry " & QueueCount & " of " & NumQueue)
  Wscript.Echo strTLine
  WScript.Echo FormatOutput("    DN") & QueueEntry.DN
  WScript.Echo FormatOutput("    Queue Guid") & QueueEntry.Guid
  Wscript.Echo FormatOutput("    Reference Guid") & QueueEntry.ReferenceGuid
  WScript.Echo FormatOutput("    Retry Count") & QueueEntry.RetryCount
  WScript.Echo FormatOutput("    MA Name") & GetMANameFromGuid(QueueEntry.MAGUID)
  WScript.Echo FormatOutput("    MA Guid") & QueueEntry.MAGUID
  WScript.Echo FormatOutput("    CS Guid") & QueueEntry.CsGUID
  WScript.Echo FormatOutput("    Originating CS Guid") & QueueEntry.OriginatingCSGuid
  Wscript.Echo FormatOutput("    Last Attempt Time") & QueueEntry.LastAttemptTime
  WScript.Echo FormatOutput("    Last Attempt Return Code") & QueueEntry.LastAttemptReturnCode
  WScript.Echo FormatOutput("    <tla rid="fim_sync_short"/> Time Received") & QueueEntry.MIISReceiveTime
  WScript.Echo FormatOutput("    Attempt Details") & vbCrLf & QueueEntry.AttemptDetails

  WScript.Echo
End Sub

Sub DisplayConnectorValues
  WScript.Echo FormatOutput("  Connector Object " & ConnectorsCount)
  Wscript.Echo strTLine
  If Len(ConnectedCsObjects.Account) > 0 Then
  WScript.Echo FormatOutput("  Account") & ConnectedCsObjects.Account
  Else
    WScript.Echo FormatOutput("  Account") & "N/A"
  End If
  If Len(ConnectedCsObjects.Domain) > 0 Then
    WScript.Echo FormatOutput("  Domain")  & ConnectedCsObjects.Domain
  Else  
    WScript.Echo FormatOutput("  Domain")  & "N/A"
  End If
  WScript.Echo FormatOutput("  MA Name") & ConnectedCsObjects.MaName
  WScript.Echo FormatOutput("  MA Partition") & ConnectedCsObjects.PartitionName
  WScript.Echo FormatOutput("  MA Guid") & ConnectedCsObjects.MaGuid
  WScript.Echo FormatOutput("  CS Guid") & ConnectedCsObjects.Guid
  If Len(ConnectedCsObjects.PasswordChangeHistory) > 0 Then 
    WScript.Echo FormatOutput("  PasswordChangeHistory") & vbCrLf & _
      ConnectedCSObjects.PasswordChangeHistory & vbcrlf
  Else
    WScript.Echo FormatOutput("  PasswordChangeHistory") & "None" & vbcrlf
  End If
  MADataAdd ConnectedCsObjects.MaName, ConnectedCsObjects.MAGuid
End Sub

Sub MADataAdd(CurrentMAName, CurrentMAGuid)
  Dim Size

  Size = Ubound(MAGuid) + 1
  ReDim Preserve MAName(Size)
  ReDim Preserve MAGuid(Size)
  MAName(Size) = CurrentMAName
  MAGuid(Size) = CurrentMAGuid
End Sub

Function GetMANameFromGuid(CurrentMAGuid)
  Dim i

  GetMANameFromGuid = "Unknown"

  For i = 0 To Ubound(MAGuid)
    If MAGuid(i) = CurrentMAGuid Then
      GetMANameFromGuid = MAName(i)
    Exit For
    End If
  Next
End Function

Function FormatOutput(DisplayString)
  Dim DisplayStringLen

  DisplayStringLen = Len(DisplayString)

  If DisplayStringLen >= BufferLen Then
    FormatOutput = DisplayString & ": "
  Else
    FormatOutput = DisplayString & Right(strPad,  BufferLen - DisplayStringLen ) & " "
  End If
   
End Function

Sub ErrorHandler (ErrorMessage)
  WScript.Echo ErrorMessage
  WScript.Quit(1)
End Sub

Sub Usage
  Wscript.echo "Password Change History Source Usage Menu:" & vbCrlf
  Wscript.echo "cscript PasswordChangeHistorySource.vbs SearchDomain SearchUser"
  Wscript.echo "             [MIIS_UserName MIIS_UserPassword MIIS_MachineName]" & vbcrlf
  Wscript.echo "SearchDomain         Domain name to search for within the CS"
  Wscript.echo "SearchUser           User name to search for within the CS"
  Wscript.echo "MIIS_UserName        Domain\UserName to connect as for a remote computer"
  Wscript.echo "MIIS_UserPassword    Password for the MIIS_UserName argument"
  Wscript.echo "MIIS_MachineName     Name of the remote computer <tla rid="fim_sync_short"/> is running on" & vbcrlf
End Sub

See Also

Reference

MIIS_PasswordChangeHistorySource Class
MIIS_PasswordChangeHistoryTarget Class
MIIS_PasswordChangeQueue Class

Concepts

Password Synchronization
Password Management
WMI Provider Overview
Using the WMI Provider