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 {
}
}
@jloutsch
Copy link

jloutsch commented Oct 1, 2019

Hello Alex, when I attempt to run this script, I get the following output:

New-AdhcResult : The term 'New-AdhcResult' is not recognized as the name of a cmdlet, function, script file, or
operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try
again.
At C:\Users\jloutsch\Test-AdhcDCDiag.ps1:196 char:13

  •         New-AdhcResult @ResultSplat
    
  •         ~~~~~~~~~~~~~~
    
    • CategoryInfo : ObjectNotFound: (New-AdhcResult:String) [], CommandNotFoundException
    • FullyQualifiedErrorId : CommandNotFoundException

@DaveyRance
Copy link

Hi Alex. In the New-AdhcResult you have a mandatory paramater of $SubCategory but in the main script you are not setting it to anything. so the script just stops asking for the Subcategory. Any chance you could update the main body to have this?

@AlexAsplund
Copy link
Author

Hi Alex. In the New-AdhcResult you have a mandatory paramater of $SubCategory but in the main script you are not setting it to anything. so the script just stops asking for the Subcategory. Any chance you could update the main body to have this?

Hi Davey!
Removed the parameter flag so it should work as expected now.

@minglani
Copy link

Hi Alex,

I am running this in a test environment on a Windows Server 2016 Std,

here is the error I am getting:

Cannot create object of type "AdhcResult". The SubCategory property was not found for the AdhcResult object. The available property is: [Source <System.String>] ,
[TestName <System.String>] , [Pass <System.Boolean>] , [Was <System.Object>] , [ShouldBe <System.Object>] , [Category <System.String>] , [Message <System.String>] , [Data
<System.Object>] , [Tags <System.String[]>]
At E:\Powershell\AD HealthCheck\Test-AdhcDcDiag.ps1:62 char:9

  •     [AdhcResult]@{
    
  •     ~~~~~~~~~~~~~~
    
    • CategoryInfo : InvalidArgument: (:) [], RuntimeException
    • FullyQualifiedErrorId : ObjectCreationError

@Raffytje
Copy link

Raffytje commented Mar 29, 2022

Hi Alex,

I am running this in a test environment on a Windows Server 2016 Std,

here is the error I am getting:

Cannot create object of type "AdhcResult". The SubCategory property was not found for the AdhcResult object. The available property is: [Source <System.String>] , [TestName <System.String>] , [Pass <System.Boolean>] , [Was <System.Object>] , [ShouldBe <System.Object>] , [Category <System.String>] , [Message <System.String>] , [Data <System.Object>] , [Tags <System.String[]>] At E:\Powershell\AD HealthCheck\Test-AdhcDcDiag.ps1:62 char:9

  •     [AdhcResult]@{
    
  •     ~~~~~~~~~~~~~~
    
    • CategoryInfo : InvalidArgument: (:) [], RuntimeException
    • FullyQualifiedErrorId : ObjectCreationError

This is solved when you use this Class in the beginning of the script:

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

@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