Skip to content

Instantly share code, notes, and snippets.

@AlexAsplund
Last active April 28, 2023 03:41
Show Gist options
  • Save AlexAsplund/c98bd06484d87a6e67a4250a182f1f49 to your computer and use it in GitHub Desktop.
Save AlexAsplund/c98bd06484d87a6e67a4250a182f1f49 to your computer and use it in GitHub Desktop.
Class AdhcResult {
[string]$Source
[string]$TestName
[bool]$Pass
$Was
$ShouldBe
[string]$Category
[string]$Message
$Data
[string[]]$Tags
}
Function New-AdhcResult {
[cmdletbinding()]
param(
# Source of the result. The computer that was tested
[parameter(ValueFromPipelineByPropertyName)]
[string]$Source = $env:COMPUTERNAME,
# Name of the test
[parameter(Mandatory,ValueFromPipelineByPropertyName)]
[string]$TestName,
# True = Test pass
[parameter(Mandatory,ValueFromPipelineByPropertyName)]
[bool]$Pass,
[parameter(ValueFromPipelineByPropertyName)]
$Was,
[parameter(ValueFromPipelineByPropertyName)]
$ShouldBe,
# General category of the test. Like "Directory Services" or "DNS"
[parameter(Mandatory,ValueFromPipelineByPropertyName)]
[string]$Category,
# A more specific subcategory like "FSMO" or "DNS Forwarding"
[parameter(ValueFromPipelineByPropertyName)]
[string]$SubCategory,
# Tags for this test like "Security", "Updates", "Logon"
[parameter(Mandatory,ValueFromPipelineByPropertyName)]
[string[]]$Tags,
# General message
[parameter(ValueFromPipelineByPropertyName)]
[string]$Message,
# Extra data to the test result. Like accountnames or SPN's etc.
[parameter(ValueFromPipelineByPropertyName)]
$Data
)
Begin {
}
Process {
[AdhcResult]@{
Source = $Source
TestName = $TestName
Pass = $Pass
Was = $Was
ShouldBe = $ShouldBe
Category = $Category
SubCategory = $SubCategory
Message = $Message
Data = $Data
Tags = $Tags
}
}
End {
}
}
Function Test-AdhcDCDiag {
[cmdletbinding()]
param(
# Name of the DC
[parameter(ValueFromPipeline)]
[string]$ComputerName,
# What DCDiag tests would you like to run?
[ValidateSet(
"All",
"Advertising",
"DNS",
"NCSecDesc",
"KccEvent",
"Services",
"NetLogons",
"CrossRefValidation",
"CutoffServers",
"CheckSecurityError",
"Intersite",
"CheckSDRefDom",
"Connectivity",
"SysVolCheck",
"Replications",
"ObjectsReplicated",
"DcPromo",
"RidManager",
"Topology",
"MachineAccount",
"LocatorCheck",
"OutboundSecureChannels",
"RegisterInDNS",
"VerifyEnterpriseReferences",
"KnowsOfRoleHolders",
"VerifyReplicas",
"VerifyReferences"
)]
[string[]]$Tests = "All",
# Excluded tests
[ValidateSet(
"Advertising",
"DNS",
"NCSecDesc",
"KccEvent",
"Services",
"NetLogons",
"CrossRefValidation",
"CutoffServers",
"CheckSecurityError",
"Intersite",
"CheckSDRefDom",
"Connectivity",
"SysVolCheck",
"Replications",
"ObjectsReplicated",
"DcPromo",
"RidManager",
"Topology",
"MachineAccount",
"LocatorCheck",
"OutboundSecureChannels",
"RegisterInDNS",
"VerifyEnterpriseReferences",
"KnowsOfRoleHolders",
"VerifyReplicas",
"VerifyReferences"
)]
[string[]]$ExcludedTests
)
Begin {
$DCDiagTests = @{
Advertising = @{}
CheckSDRefDom = @{}
CheckSecurityError = @{
ExtraArgs = @(
"/replsource:$((Get-ADDomainController -Filter *).HostName | ? {$_ -notmatch $env:computername} | Get-Random)"
)
}
Connectivity = @{}
CrossRefValidation = @{}
CutoffServers = @{}
DcPromo = @{
ExtraArgs = @(
"/ReplicaDC",
"/DnsDomain:$((Get-ADDomain).DNSRoot)",
"/ForestRoot:$((Get-ADDomain).Forest)"
)
}
DNS = @{}
SysVolCheck = @{}
LocatorCheck = @{}
Intersite = @{}
KccEvent = @{}
KnowsOfRoleHolders = @{}
MachineAccount = @{}
NCSecDesc = @{}
NetLogons = @{}
ObjectsReplicated = @{}
OutboundSecureChannels = @{}
RegisterInDNS = @{
ExtraArgs = "/DnsDomain:$((Get-ADDomain).DNSRoot)"
}
Replications = @{}
RidManager = @{}
Services = @{}
Topology = @{}
VerifyEnterpriseReferences = @{}
VerifyReferences = @{}
VerifyReplicas = @{}
}
$TestsToRun = $DCDiagTests.Keys | Where-Object {$_ -notin $ExcludedTests}
If($Tests -ne 'All'){
$TestsToRun = $Tests
}
if(($Tests | Measure-Object).Count -gt 1 -and $Tests -contains "All"){
Write-Error "Invalid Tests parameter value: You can't use 'All' with other tests." -ErrorAction Stop
}
Write-Verbose "Executing tests: $($DCDiagTests.Keys -join ", ")"
}
Process {
if(![string]::IsNullOrEmpty($ComputerName)) {
$ServerArg = "/s:$ComputerName"
}
else {
$ComputerName = $env:COMPUTERNAME
$ServerArg = "/s:$env:COMPUTERNAME"
}
Write-Verbose "Starting DCDIAG on $ComputerName"
$TestResults = @()
$TestsToRun | Foreach {
Write-Verbose "Starting test $_ on $ComputerName"
$TestName = $_
$ExtraArgs = $DCDiagTests[$_].ExtraArgs
if($_ -in @("DcPromo", "RegisterInDNS")){
if($env:COMPUTERNAME -ne $ComputerName){
Write-Verbose "Test cannot be performed remote, invoking dcdiag"
$Output = Invoke-Command -ComputerName $ComputerName -ArgumentList @($TestName,$ExtraArgs) -ScriptBlock {
$TestName = $args[0]
$ExtraArgs = $args[1]
dcdiag /test:$TestName $ExtraArgs
}
}
else {
$Output = dcdiag /test:$TestName $ExtraArgs
}
}
else {
$Output = dcdiag /test:$TestName $ExtraArgs $ServerArg
}
$Fails = ($Output | Select-String -AllMatches -Pattern "fail" | Measure-Object).Count
$Passes = ($Output | Select-String -AllMatches -Pattern "passed" | Measure-Object).Count
$Pass = ($Fails -eq 0 -and $Passes -gt 0)
$ResultSplat = @{
Source = $ComputerName
TestName = "$_"
Pass = ($Fails -eq 0 -and $Passes -gt 0)
Was = $Fails,$Passes
ShouldBe = 0,0
Category = "DCDIAG"
Message = "$Output[-1]"
Data = $Output
Tags = @('DCDIAG',$_)
}
New-AdhcResult @ResultSplat
}
$TestResults
}
End {
}
}
@ZummiGummi
Copy link

Following on from what @Raffytje found, it was still generating the same error for me, until I saw the SubCategory property was missing.
Fixed class is below.

Class AdhcResult {
    [string]$Source
    [string]$TestName
    [bool]$Pass
    $Was
    $ShouldBe
    [string]$Category
    [string]$SubCategory
    [string]$Message
    $Data
    [string[]]$Tags

}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment