Created
July 15, 2025 17:07
-
-
Save FriedrichWeinmann/c191ddb9ade52332978da515ae523ce4 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| function Resolve-DCServer { | |
| [CmdletBinding()] | |
| param ( | |
| [string[]] | |
| $Server, | |
| [PSCredential] | |
| $Credential, | |
| [hashtable] | |
| $Cache = @{} | |
| ) | |
| begin { | |
| $adParam = @{ } | |
| if ($Credential) { $adParam.Credential = $Credential } | |
| } | |
| process { | |
| foreach ($serverEntry in $Server) { | |
| if ($Cache[$serverEntry]) { | |
| $Cache[$serverEntry] | |
| continue | |
| } | |
| $resolve = $serverEntry | |
| if ($serverEntry -match '^CN=.+?,DC=') { | |
| $parts = $serverEntry -split ',' | Where-Object { $_ -match '^CN=|^DC=' } | |
| $Cache[$serverEntry] = $parts -replace '^.+?=' -join '.' | |
| $Cache[$serverEntry] | |
| continue | |
| } | |
| if ($serverEntry -match '^DC=') { $resolve = $serverEntry -replace ',DC=', '.' -replace '^DC=' } | |
| else { $resolve = $serverEntry } | |
| $domain = Get-ADDomain @adParam -Server $resolve | |
| if ($domain.ReplicaDirectoryServers -contains $resolve) { | |
| $Cache[$serverEntry] = $resolve | |
| $Cache[$serverEntry] | |
| continue | |
| } | |
| if ($target = $domain.ReplicaDirectoryServers | Where-Object { $_ -match "^$($resolve)\." }) { | |
| $Cache[$serverEntry] = $target | |
| $Cache[$serverEntry] | |
| continue | |
| } | |
| foreach ($replica in $domain.ReplicaDirectoryServers) { | |
| $Cache[$serverEntry] = $replica | |
| $Cache[$serverEntry] | |
| } | |
| } | |
| } | |
| } | |
| function Get-DCDiagnosticSetting { | |
| [CmdletBinding()] | |
| param ( | |
| [Parameter(ValueFromPipeline = $true, Mandatory = $true)] | |
| [string[]] | |
| $Server, | |
| [ArgumentCompleter({'Backup', 'Claims-BasedAccessControl', 'DirectoryAccess', 'DSRPCClient', 'DSRPCServer', 'DSSchema', 'ExDSInterfaceEvents', 'FieldEngineering', 'GarbageCollection', 'GlobalCatalog', 'GroupCaching', 'Initialization/Termination', 'InternalConfiguration', 'InternalProcessing', 'Inter-siteMessaging', 'KnowledgeConsistencyChecker', 'LDAPInterfaceEvents', 'Linked-ValueReplication', 'MAPIInterfaceEvents', 'NameResolution', 'PDCPasswordUpdateNotifications', 'PerformanceCounters', 'ReplicationEvents', 'SecurityEvents', 'ServiceControl', 'Setup', 'TransformationEngine'})] | |
| [string] | |
| $Name = '*', | |
| [PSCredential] | |
| $Credential | |
| ) | |
| begin { | |
| $computers = [System.Collections.Generic.List[object]]::new() | |
| $adParam = @{ } | |
| if ($Credential) { $adParam.Credential = $Credential } | |
| } | |
| process { | |
| foreach ($target in Resolve-DCServer -Server $Server @adParam) { | |
| $computers.Add($target) | |
| } | |
| } | |
| end { | |
| $targets = $computers | Sort-Object -Unique | |
| Invoke-Command -ComputerName $targets @adParam -ScriptBlock { | |
| if (-not (Test-Path 'HKLM:\SYSTEM\CurrentControlSet\Services\NTDS\Diagnostics')) { | |
| Write-Warning "Not a DC: $env:COMPUTERNAME" | |
| return | |
| } | |
| $entries = '1 Knowledge Consistency Checker', '2 Security Events', '3 ExDS Interface Events', '4 MAPI Interface Events', '5 Replication Events', '6 Garbage Collection', '7 Internal Configuration', '8 Directory Access', '9 Internal Processing', '10 Performance Counters', '11 Initialization/Termination', '12 Service Control', '13 Name Resolution', '14 Backup', '15 Field Engineering', '16 LDAP Interface Events', '17 Setup', '18 Global Catalog', '19 Inter-site Messaging', '20 Group Caching', '21 Linked-Value Replication', '22 DS RPC Client', '23 DS RPC Server', '24 DS Schema', '25 Transformation Engine', '26 Claims-Based Access Control', '27 PDC Password Update Notifications' | |
| $properties = Get-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\NTDS\Diagnostics' | |
| foreach ($entry in $entries) { | |
| [PSCustomObject]@{ | |
| ComputerName = $env:COMPUTERNAME | |
| Name = $entry -replace '\d|\s' | |
| Value = $properties.$entry | |
| FullName = $entry | |
| } | |
| } | |
| } | Select-Object ComputerName, Name, Value, FullName | Where-Object Name -like $Name | |
| } | |
| } | |
| function Set-DCDiagnosticSetting { | |
| [CmdletBinding()] | |
| param ( | |
| [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] | |
| [Alias('ComputerName')] | |
| [string[]] | |
| $Server, | |
| [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)] | |
| [ValidateSet('Backup', 'Claims-BasedAccessControl', 'DirectoryAccess', 'DSRPCClient', 'DSRPCServer', 'DSSchema', 'ExDSInterfaceEvents', 'FieldEngineering', 'GarbageCollection', 'GlobalCatalog', 'GroupCaching', 'Initialization/Termination', 'InternalConfiguration', 'InternalProcessing', 'Inter-siteMessaging', 'KnowledgeConsistencyChecker', 'LDAPInterfaceEvents', 'Linked-ValueReplication', 'MAPIInterfaceEvents', 'NameResolution', 'PDCPasswordUpdateNotifications', 'PerformanceCounters', 'ReplicationEvents', 'SecurityEvents', 'ServiceControl', 'Setup', 'TransformationEngine')] | |
| [string[]] | |
| $Name, | |
| [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)] | |
| [ValidateRange(0, 5)] | |
| [int] | |
| $Value, | |
| [PSCredential] | |
| $Credential | |
| ) | |
| begin { | |
| $serverMap = @{ } | |
| $workload = [System.Collections.Generic.List[object]]::new() | |
| $mapping = @{ | |
| 'KnowledgeConsistencyChecker' = '1 Knowledge Consistency Checker' | |
| 'SecurityEvents' = '2 Security Events' | |
| 'ExDSInterfaceEvents' = '3 ExDS Interface Events' | |
| 'MAPIInterfaceEvents' = '4 MAPI Interface Events' | |
| 'ReplicationEvents' = '5 Replication Events' | |
| 'GarbageCollection' = '6 Garbage Collection' | |
| 'InternalConfiguration' = '7 Internal Configuration' | |
| 'DirectoryAccess' = '8 Directory Access' | |
| 'InternalProcessing' = '9 Internal Processing' | |
| 'PerformanceCounters' = '10 Performance Counters' | |
| 'Initialization/Termination' = '11 Initialization/Termination' | |
| 'ServiceControl' = '12 Service Control' | |
| 'NameResolution' = '13 Name Resolution' | |
| 'Backup' = '14 Backup' | |
| 'FieldEngineering' = '15 Field Engineering' | |
| 'LDAPInterfaceEvents' = '16 LDAP Interface Events' | |
| 'Setup' = '17 Setup' | |
| 'GlobalCatalog' = '18 Global Catalog' | |
| 'Inter-siteMessaging' = '19 Inter-site Messaging' | |
| 'GroupCaching' = '20 Group Caching' | |
| 'Linked-ValueReplication' = '21 Linked-Value Replication' | |
| 'DSRPCClient' = '22 DS RPC Client' | |
| 'DSRPCServer' = '23 DS RPC Server' | |
| 'DSSchema' = '24 DS Schema' | |
| 'TransformationEngine' = '25 Transformation Engine' | |
| 'Claims-BasedAccessControl' = '26 Claims-Based Access Control' | |
| 'PDCPasswordUpdateNotifications' = '27 PDC Password Update Notifications' | |
| } | |
| $adParam = @{ } | |
| if ($Credential) { $adParam.Credential = $Credential } | |
| } | |
| process { | |
| $computers = Resolve-DCServer -Server $Server @adParam | Sort-Object -Unique | |
| foreach ($computer in $computers) { | |
| foreach ($task in $Name) { | |
| $workload.Add( | |
| [PSCustomObject]@{ | |
| ComputerName = $computer | |
| FullName = $mapping[$task] | |
| Value = $Value | |
| } | |
| ) | |
| } | |
| } | |
| } | |
| end { | |
| Invoke-Command -ComputerName ($workload.ComputerName | Sort-Object -Unique) @adParam -ScriptBlock { | |
| param ($Data) | |
| $todos = $Data.Data | Where-Object ComputerName -match "^$($env:COMPUTERNAME)$|^$($env:COMPUTERNAME)\." | |
| foreach ($todo in $todos) { | |
| Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\NTDS\Diagnostics' -Name $todo.FullName -Value $todo.Value | |
| } | |
| } -ArgumentList @{ Data = $workload } | |
| } | |
| } | |
| function Get-DCReplication { | |
| [CmdletBinding(DefaultParameterSetName = 'Identity')] | |
| param ( | |
| [Parameter(Mandatory = $true)] | |
| [string[]] | |
| $Server, | |
| [PSCredential] | |
| $Credential, | |
| [Parameter(Mandatory = $true, ParameterSetName = 'Identity')] | |
| [Parameter(ParameterSetName = 'Range')] | |
| [string] | |
| $Identity, | |
| [Parameter(Mandatory = $true, ParameterSetName = 'Range')] | |
| [datetime] | |
| $Start, | |
| [datetime] | |
| [Parameter(Mandatory = $true, ParameterSetName = 'Range')] | |
| $End | |
| ) | |
| begin { | |
| $adParam = @{ } | |
| if ($Credential) { $adParam.Credential = $Credential } | |
| #region Implementing Code | |
| $code = { | |
| param ($Data) | |
| Update-TypeData -TypeName 'System.Diagnostics.Eventing.Reader.EventLogRecord' -SerializationDepth 3 -Force | |
| #region Utility Functions | |
| function New-ADChangeEntity { | |
| [CmdletBinding()] | |
| param ( | |
| [Parameter(Mandatory = $true)] | |
| [hashtable] | |
| $Items, | |
| [Parameter(Mandatory = $true)] | |
| [string] | |
| $Identity, | |
| [ValidateSet('Updated', 'Created', 'Deleted', 'Other')] | |
| [string] | |
| $Type = 'Other' | |
| ) | |
| $Items[$Identity] = [PSCustomObject]@{ | |
| PSTypeName = 'DCReplication.ObjectChange' | |
| Identity = $Identity | |
| ObjectID = '' | |
| Type = $Type | |
| Updated = @() | |
| Skipped = @() | |
| Events = @() | |
| } | |
| } | |
| function Set-ChangeEntitySkipped { | |
| [CmdletBinding()] | |
| param ( | |
| [Parameter(Mandatory = $true)] | |
| [hashtable] | |
| $Items, | |
| [Parameter(Mandatory = $true)] | |
| [string] | |
| $Property, | |
| [Parameter(Mandatory = $true)] | |
| [string] | |
| $Identity | |
| ) | |
| if (-not $Items[$Identity]) { New-ADChangeEntity -Items $Items -Identity $Identity } | |
| $Items[$Identity].Skipped += $Property | |
| } | |
| function Set-ChangeEntityChanged { | |
| [CmdletBinding()] | |
| param ( | |
| [Parameter(Mandatory = $true)] | |
| [hashtable] | |
| $Items, | |
| [Parameter(Mandatory = $true)] | |
| [string] | |
| $Property, | |
| [Parameter(Mandatory = $true)] | |
| [string] | |
| $Identity | |
| ) | |
| if (-not $Items[$Identity]) { New-ADChangeEntity -Items $Items -Identity $Identity } | |
| $Items[$Identity].Updated += $Property | |
| $Items[$Identity].Type = 'Updated' | |
| } | |
| function Set-ReplicationTarget { | |
| [CmdletBinding()] | |
| param ( | |
| [Parameter(Mandatory = $true)] | |
| [hashtable] | |
| $RepData, | |
| [Parameter(Mandatory = $true)] | |
| [string] | |
| $Identifier | |
| ) | |
| # Identifier: 3f06bc30-7a85-4c9b-95eb-5ccd7a945878 (AD1D4DCDC2.contoso.com) | |
| $RepData.Target = $Identifier -replace '^.+?\(' -replace '\).*$' | |
| $RepData.TargetID = $Identifier -replace '\s.+' | |
| } | |
| #endregion Utility Functions | |
| #region Identify Replication Threads | |
| $filterBase = @' | |
| <QueryList> | |
| <Query Id="0" Path="Directory Service"> | |
| <Select Path="Directory Service"> | |
| *[ | |
| {0} | |
| ] | |
| </Select> | |
| </Query> | |
| </QueryList> | |
| '@ | |
| if ($Data.Identity) { | |
| $filterParts = @(' System[(EventID=1239 or EventID=1240 or EventID=1412 or EventID=1413 or EventID=1366)]') | |
| if ($Data.Start -and $Data.End) { | |
| $filterParts += " and System[TimeCreated[@SystemTime>='{0:yyyy-MM-ddTHH:mm:ss.fffZ}' and @SystemTime<='{1:yyyy-MM-ddTHH:mm:ss.fffZ}']]]" -f $Data.Start.ToUniversalTime(), $Data.End.ToUniversalTime() | |
| } | |
| $filterParts += " and EventData[Data='{0}']" -f $Data.Identity | |
| $filter = $filterBase -f ($filterParts -join "`n") | |
| } | |
| else { | |
| $filterParts = @( | |
| ' System[(EventID=1239 or EventID=1240 or EventID=1412 or EventID=1413 or EventID=1366)]' | |
| (" and System[TimeCreated[@SystemTime>='{0:yyyy-MM-ddTHH:mm:ss.fffZ}' and @SystemTime<='{1:yyyy-MM-ddTHH:mm:ss.fffZ}']]]" -f $Data.Start.ToUniversalTime(), $Data.End.ToUniversalTime()) | |
| ) | |
| $filter = $filterBase -f ($filterParts -join "`n") | |
| } | |
| $events = Get-WinEvent -FilterXml $filter -ErrorAction SilentlyContinue | |
| #endregion Identify Replication Threads | |
| #region Process Individual Replications | |
| $filterCorellationTemplate = @' | |
| <QueryList> | |
| <Query Id="0" Path="Directory Service"> | |
| <Select Path="Directory Service">*[System[Correlation[@ActivityID='{0}']]]</Select> | |
| </Query> | |
| </QueryList> | |
| '@ | |
| foreach ($activity in $events.ActivityId | Sort-Object -Unique) { | |
| $filter = $filterCorellationTemplate -f "{$activity}" | |
| $repEvents = Get-WinEvent -FilterXml $filter | Sort-Object RecordId | |
| $repData = @{ | |
| NamingContext = $null | |
| Target = $null | |
| TargetID = $null | |
| USN = $null | |
| Flags = $null | |
| Sensitivity = $null | |
| Operation = $null | |
| ControlFlags = $null | |
| ActiveGetNCSessions = $null | |
| OptionsHex = $null | |
| ActiveReplicaSyncSessions = $null | |
| } | |
| $repSummary = @{ | |
| Status = 0 | |
| NumberObjects = 0 | |
| TotalBytes = 0 | |
| USN = $null | |
| ExtendedReturn = $null | |
| TimeTakenMS = 0 | |
| RemainingGetNCSessions = $null | |
| RemainingReplicaSyncSessions = $null | |
| } | |
| $repRequest = @{ | |
| Partition = '' | |
| NetworkAddress = '' | |
| OptionsHex = $null | |
| } | |
| $repSyncData = $null | |
| $systemUpdates = @() | |
| $objectUpdates = @() | |
| $other = @() | |
| $messages = [System.Collections.ArrayList]@() | |
| $unknownEvents = [System.Collections.ArrayList]@() | |
| $items = @{} | |
| foreach ($eventItem in $repEvents) { | |
| switch ($eventItem.Id) { | |
| #region 1060 Agent Request Completed | |
| 1060 { | |
| $null = $messages.Add($eventItem.Message) | |
| } | |
| #endregion 1060 Agent Request Completed | |
| #region 1068 Replication Request | |
| 1068 { | |
| $repRequest.Partition = $eventItem.Properties[0].Value | |
| $repRequest.NetworkAddress = $eventItem.Properties[1].Value | |
| $repRequest.OptionsHex = $eventItem.Properties[2].Value | |
| $null = $messages.Add($eventItem.Message.Split("`n")[0]) | |
| } | |
| #endregion 1068 Replication Request | |
| #region 1070 Replication Start (Replica) | |
| 1070 { | |
| $repData.NamingContext = $eventItem.Properties[0].Value | |
| Set-ReplicationTarget -RepData $repData -Identifier $eventItem.Properties[1].Value | |
| $repData.OptionsHex = $eventItem.Properties[2].Value | |
| $repData.ActiveReplicaSyncSessions = $eventItem.Properties[3].Value | |
| $null = $messages.Add("$($repData.Target): $($eventItem.Message.Split("`n")[0])") | |
| } | |
| #endregion 1070 Replication Start (Replica) | |
| #region 1072 Replication Start | |
| 1072 { | |
| $repData.NamingContext = $eventItem.Properties[1].Value | |
| Set-ReplicationTarget -RepData $repData -Identifier $eventItem.Properties[0].Value | |
| $repData.USN = $eventItem.Properties[2].Value | |
| $repData.Flags = $eventItem.Properties[3].Value | |
| $repData.Sensitivity = $eventItem.Properties[4].Value | |
| $repData.Operation = $eventItem.Properties[5].Value | |
| $repData.ControlFlags = $eventItem.Properties[6].Value | |
| $repData.ActiveGetNCSessions = $eventItem.Properties[7].Value | |
| $null = $messages.Add("$($repData.Target): $($eventItem.Message.Split("`n")[0])") | |
| } | |
| #endregion 1072 Replication Start | |
| #region 1073 Replication Summary | |
| 1073 { | |
| $repSummary.NumberObjects = $eventItem.Properties[0].Value | |
| $repSummary.TotalBytes = $eventItem.Properties[1].Value | |
| $repSummary.USN = $eventItem.Properties[2].Value | |
| $repSummary.ExtendedReturn = $eventItem.Properties[3].Value | |
| $repSummary.TimeTakenMS = $eventItem.Properties[4].Value | |
| $repSummary.RemainingGetNCSessions = $eventItem.Properties[5].Value | |
| } | |
| #endregion 1073 Replication Summary | |
| #region 1239 Attribute Skipped | |
| 1239 { | |
| $null = $messages.Add("$($eventItem.Properties[1].Value) --> $($eventItem.Properties[0].Value -replace '^.+?\(|\).*$'): $($eventItem.Message.Split("`n")[0])") | |
| Set-ChangeEntitySkipped -Items $items -Property ($eventItem.Properties[0].Value -replace '^.+?\(|\).*$') -Identity $eventItem.Properties[1].Value | |
| Set-ReplicationTarget -RepData $repData -Identifier $eventItem.Properties[3].Value | |
| } | |
| #endregion 1239 Attribute Skipped | |
| #region 1240 Attribute Updated | |
| 1240 { | |
| $null = $messages.Add("$($eventItem.Properties[1].Value) --> $($eventItem.Properties[0].Value -replace '^.+?\(|\).*$'): $($eventItem.Message.Split("`n")[0])") | |
| Set-ChangeEntityChanged -Items $items -Property ($eventItem.Properties[0].Value -replace '^.+?\(|\).*$') -Identity $eventItem.Properties[1].Value | |
| Set-ReplicationTarget -RepData $repData -Identifier $eventItem.Properties[3].Value | |
| } | |
| #endregion 1240 Attribute Updated | |
| #region 1360 Replica Sync Completed | |
| 1360 { | |
| $repSummary.Status = $eventItem.Properties[0].Value | |
| $repSummary.TimeTakenMS = $eventItem.Properties[1].Value | |
| $repSummary.RemainingReplicaSyncSessions = $eventItem.Properties[2].Value | |
| $null = $messages.Add("$($repData.Target): $($eventItem.Message.Split("`n")[0]). Status: $($repSummary.Status). Duration: $($repSummary.TimeTakenMS)ms") | |
| } | |
| #endregion 1360 Replica Sync Completed | |
| #region 1363 Up-to-Dateness Vector Change | |
| 1363 { | |
| $change = [PSCustomObject]@{ | |
| Type = 'Up-to-Dateness Vector Update' | |
| HostName = $env:COMPUTERNAME | |
| TargetID = $eventItem.Properties[0].Value -replace '\s.+$' | |
| TargetHost = $Data.DCMap[($eventItem.Properties[0].Value -replace '\s.+$')] | |
| Partition = $eventItem.Properties[3].Value | |
| USNOld = $eventItem.Properties[1].Value | |
| USNNew = $eventItem.Properties[2].Value | |
| } | |
| $systemUpdates += $change | |
| $null = $messages.Add("$($eventItem.Message.Split("`n")[0]): $($change.TargetID) | $($change.TargetHost) | USN (Old): $($change.USNOld) | USN (New): $($change.USNNew)") | |
| } | |
| #endregion 1363 Up-to-Dateness Vector Change | |
| #region 1364 Update USN for DirectoryService | |
| 1364 { | |
| $change = [PSCustomObject]@{ | |
| Type = 'USN Vector Update' | |
| Hostname = $env:COMPUTERNAME | |
| TargetID = $eventItem.Properties[0].Value -replace '\s.+$' | |
| TargetHost = $Data.DCMap[($eventItem.Properties[0].Value -replace '\s.+$')] | |
| ObjectUSNOld = $eventItem.Properties[1].Value | |
| ObjectUSNNew = $eventItem.Properties[3].Value | |
| PropertyUSNOld = $eventItem.Properties[2].Value | |
| PropertyUSNNew = $eventItem.Properties[4].Value | |
| } | |
| $systemUpdates += $change | |
| $null = $messages.Add("$($eventItem.Message.Split("`n")[0]): $($change.TargetID) | $($change.TargetHost) | Object USN: $($change.ObjectUSNOld) > $($change.ObjectUSNNew) | Property USN: $($change.PropertyUSNOld) > $($change.PropertyUSNNew)") | |
| } | |
| #endregion 1364 Update USN for DirectoryService | |
| #region 1366 Object Update | |
| 1366 { | |
| $change = [PSCustomObject]@{ | |
| Type = 'Object Update' | |
| Object = $eventItem.Properties[0].Value | |
| ObjectID = $eventItem.Properties[1].Value | |
| } | |
| if (-not $items[$change.Object]) { New-ADChangeEntity -Items $items -Type Updated } | |
| $items[$change.Object].ObjectID = $change.ObjectID | |
| $objectUpdates += $change | |
| $null = $messages.Add("$($eventItem.Message.Split("`n")[0]): $($change.Object) | $($change.ObjectID)") | |
| } | |
| #endregion 1366 Object Update | |
| #region 1412 Attribute Updated on Replica | |
| 1412 { | |
| $change = [PSCustomObject]@{ | |
| Type = 'Object Property Update' | |
| Object = $eventItem.Properties[1].Value | |
| ObjectID = $eventItem.Properties[2].Value | |
| Property = $eventItem.Properties[0].Value -replace '^.+?\(|\).*$' | |
| OriginVersion = $eventItem.Properties[3].Value | |
| OriginTimestamp = $eventItem.Properties[4].Value | |
| OriginUSN = $eventItem.Properties[5].Value | |
| } | |
| Set-ChangeEntityChanged -Items $items -Property $change.Property -Identity $change.Object | |
| $items[$change.Object].ObjectID = $change.ObjectID | |
| $objectUpdates += $change | |
| $null = $messages.Add("$($change.Object) --> $($change.Property): $($eventItem.Message.Split("`n")[0])") | |
| } | |
| #endregion 1412 Attribute Updated on Replica | |
| #region 1413 Attribute Update Skipped on Replica | |
| 1413 { | |
| $property = $eventItem.Properties[0].Value -replace '^.+?\(|\).*$' | |
| Set-ChangeEntitySkipped -Items $items -Property $property -Identity $eventItem.Properties[1].Value | |
| $items[$eventItem.Properties[1].Value].ObjectID = $eventItem.Properties[2].Value | |
| $null = $messages.Add("$($eventItem.Properties[1].Value) --> $($property): $($eventItem.Message.Split("`n")[0])") | |
| } | |
| #endregion 1413 Attribute Update Skipped on Replica | |
| #region 2986 Replica Sync Summary | |
| 2986 { | |
| $change = [PSCustomObject]@{ | |
| Type = 'Replica Packet Update Summary' | |
| HostName = $env:COMPUTERNAME | |
| NamingContext = $eventItem.Properties[0].Value | |
| NumberObjects = $eventItem.Properties[1].Value | |
| NumberLinks = $eventItem.Properties[2].Value | |
| Return = $eventItem.Properties[3].Value | |
| ExtendedReturn = $eventItem.Properties[4].Value | |
| TimeTakenMS = $eventItem.Properties[5].Value | |
| } | |
| $other += $change | |
| $null = $messages.Add("Applied Replica Packet Changes. | Objects: $($change.NumberObjects) | Links: $($change.NumberLinks) | Duration: $($change.TimeTakenMS)ms") | |
| } | |
| #endregion 2986 Replica Sync Summary | |
| #region 3008 Replica Sync of Partition Summary | |
| 3008 { | |
| $repSyncData = [PSCustomObject]@{ | |
| HostName = $env:COMPUTERNAME | |
| NamingContext = $eventItem.Properties[0].Value | |
| SourceDirectory = $eventItem.Properties[1].Value | |
| ErrorCode = $eventItem.Properties[2].Value | |
| TotalBytes = $eventItem.Properties[3].Value | |
| NetworkPackets = $eventItem.Properties[4].Value | |
| ObjectsReceived = $eventItem.Properties[5].Value | |
| ObjectsApplied = $eventItem.Properties[6].Value | |
| ValuesReceived = $eventItem.Properties[7].Value | |
| ValuesApplied = $eventItem.Properties[8].Value | |
| ReplicationCycleMS = $eventItem.Properties[9].Value | |
| ApplicationTimeMS = $eventItem.Properties[10].Value | |
| ReplicationFlags = $eventItem.Properties[11].Value | |
| CorrelationID = $eventItem.Properties[12].Value | |
| } | |
| $null = $messages.Add("Completed a replica synchronization of '$($repSyncData.NamingContext)'. Bytes: $($repSyncData.TotalBytes) | Packets $($repSyncData.NetworkPackets) | Objects R: $($repSyncData.ObjectsReceived) / A: $($repSyncData.ObjectsApplied) | Values R: $($repSyncData.ValuesReceived) / A: $($repSyncData.ValuesApplied) | Rep Cycle: $($repSyncData.ReplicationCycleMS)ms | Application: $($repSyncData.ApplicationTimeMS)ms") | |
| } | |
| #endregion 3008 Replica Sync of Partition Summary | |
| #region Unexpected | |
| default { | |
| $null = $messages.Add($eventItem.Message.Split("`n")[0]) | |
| $null = $unknownEvents.Add($eventItem) | |
| } | |
| #endregion Unexpected | |
| } | |
| } | |
| [PSCustomObject]@{ | |
| ComputerName = $env:COMPUTERNAME | |
| Target = $repData.Target | |
| ReplMetaData = $repData | |
| ReplSummary = $repSummary | |
| ReplRequest = $repRequest | |
| ReplicaData = $repSyncData | |
| CountEvents = $repEvents.Count | |
| Start = $repEvents[0].TimeCreated | |
| End = $repEvents[-1].TimeCreated | |
| Items = $items.Values | |
| SystemUpdates = $systemUpdates | |
| ObjectUpdates = $objectUpdates | |
| OtherData = $other | |
| Messages = $messages.ToArray() | |
| UnexpectedEvents = $unknownEvents.ToArray() | |
| Events = $repEvents | |
| } | |
| } | |
| #endregion Process Individual Replications | |
| } | |
| #endregion Implementing Code | |
| } | |
| process { | |
| $computers = Resolve-DCServer -Server $Server @adParam | Sort-Object -Unique | |
| $dcMap = @{} | |
| Invoke-Command -ComputerName $computers -ScriptBlock { Get-ADDomainController -Server localhost -Filter * | Select-Object HostName, InvocationId } | ForEach-Object { | |
| $dcMap[$_.InvocationId] = $_.HostName | |
| } | |
| Invoke-Command -ComputerName $computers @adParam -ScriptBlock $code -ArgumentList @{ | |
| Identity = $Identity | |
| Start = $Start | |
| End = $End | |
| DCMap = $dcMap | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment