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 {
}
}
@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