---
title: "Active Directory Health Check with PowerShell Framework"
description: "Use a pre-built PowerShell framework for Active Directory health checks and output results to Pester and PRTG."
canonical: "https://adamtheautomator.com/active-directory-health-check-2/"
---

# Active Directory Health Check with PowerShell Framework

> Use a pre-built PowerShell framework for Active Directory health checks and output results to Pester and PRTG.

Source: https://adamtheautomator.com/active-directory-health-check-2/

---

ATA Learning

Tap to hide

[

ATA Learning

](/)

*   [Home](/)
*   [Tutorials](/tutorials/)
*   [Instructors](/author/)
*   [Advertising](/advertising/)
*   [Recommended Resources](/resources/)
*   [About Adam](/about-adam/)

Search for:  

*   [](https://twitter.com/adbertram)
*   [](https://github.com/Adam-the-Automator)
*   [](https://www.linkedin.com/company/adam-the-automator-llc)
*   [](/feed/)

![Active Directory Health Check with PowerShell Framework](https://adamtheautomator.com/wp-content/uploads/2019/09/active-directory-health-1.jpg)

# Active Directory Health Check with PowerShell Framework

[![](https://secure.gravatar.com/avatar/f60574d4bd6fddeac77731265286c1746662129f38c23248eb7d53d4084503d6?s=192&d=mm&r=g)Alex Asplund](https://adamtheautomator.com/author/alex/)26 September 201910 min. read

Categories: [IT Ops](/category/it-ops/)

Tags:[Active Directory](/tag/active-directory/)[PowerShell](/tag/powershell/)

Table of Contents

*   [Defining Output](#defining-output)
*   [Running the AD Health Check Tool](#running-the-ad-health-check-tool)
*   [Displaying AD Health Check Results in Tools](#displaying-ad-health-check-results-in-tools)
*   [Creating an nUnit XML File with Pester](#creating-an-nunit-xml-file-with-pester)
*   [Building an HTML Report with the Extent Tool](#building-an-html-report-with-the-extent-tool)
*   [Ingesting AD Health Check Results into PRTG](#ingesting-ad-health-check-results-into-prtg)
*   [Prerequisites](#prerequisites)
*   [Scheduling the Active Directory Health Check Script](#scheduling-the-active-directory-health-check-script)
*   [Creating an AD User Account](#creating-an-ad-user-account)
*   [Creating a Group-Managed Service Account](#creating-a-group-managed-service-account)
*   [Creating a KDS Root Key](#creating-a-kds-root-key)
*   [Creating the gMSA](#creating-the-gmsa)
*   [Installing and Testing the gMSA](#installing-and-testing-the-gmsa)
*   [Giving the gMSA Permission to Run as a Batch Job](#giving-the-gmsa-permission-to-run-as-a-batch-job)
*   [Creating the Scheduled Task](#creating-the-scheduled-task)
*   [Summary](#summary)
*   [Further Reading](#further-reading)

_This is part II of a two-part series on Active Directory health checks. Although not required reading, if you’d like to learn how the PowerShell script you’ll be learning about in this article was built, you’re encouraged to check out [Building an Active Directory Health Check Tool \[In-Depth\]: Part I](https://adamtheautomator.com/active-directory-health-check/)._

In Part I, you learned how many different multiple tests and why they’re important. Let’s now pull them all together and build a tool. In this part, you’ll convert all of the Active Directory health checks explained in Part I into a test framework. You’ll also learn how to output results of various AD health checks to tools like Pester and a monitoring tool called [PRTG](https://www.paessler.com/prtg).

> _To follow along or to see a finished version of the tool you’re going to learn about in this article, download the [ADHealthCheck-NoResult.ps1](https://gist.github.com/AlexAsplund/28f6c3ef42418902885cde1b83ebc260) script from GitHub._

## Defining Output

Having a common object type and an easy way of generating it will make it much easier to convert test results to the tool of your choice.

To create a uniform output for all potential tools, I’ve chosen to use a [PowerShell class](https://devblogs.microsoft.com/scripting/powershell-5-create-simple-class/). Although not required, it’s the approach I’ve opted to take here. The main point is to ensure all AD health checks return the same type of output.

A PowerShell class is a schema that defines how a PowerShell object should look and what it should do. Each line you see below represents a property the objects return will have. You can see below I’m planning on each AD health check to return ten properties.

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

> _To expedite this class-creation, I’ll be using a helper function called [New-AdhcResult](https://gist.github.com/AlexAsplund/0007914a0054ac0e22cf2e6bc600ed66). This function creates the class and everything you’ll need to follow along. This function will output a custom `[AdhcResult]` type object._

## Running the AD Health Check Tool

First, download and copy the [AD health check script](https://gist.github.com/AlexAsplund/28f6c3ef42418902885cde1b83ebc260) to a domain controller. Open it up with the PowerShell ISE and run it. This portion of the tool will not return any information.

Ths script will run and store the result of each check in the `$TestResults` variable as multiple `[AdhcResult]` objects. You will use these objects later on for generating reports or outputting it into various tools. Holding health check results in a variable like this allows you to add more results should you choose to create another one and use the `New-AdHcResult` command.

Once the script finishes running, you should now have a complete set of AD health check objects stored in the `$TestResults` variable. You can now run `$TestResults` from the console and see the raw results.

## Displaying AD Health Check Results in Tools

Since all of the checks are in a common object type, you can better inspect them through a couple tools like Pester and PRTG.

In this section, you’re going to learn how to create an HTML report with a tool called [extent](https://github.com/extent-framework/extentreports-dotnet-cli) and to display the report in PRTG.

### Creating an nUnit XML File with Pester

You first need to convert the PowerShell objects in a format your tools can understand. Most tools understand XML or, more specifically, nUnit XML. This is a format that you can import into various tools to display results.

Since you’re working with PowerShell, you’ll use the Pester testing framework to read the output from the AD health check script and to generate an nUnit XML file

Start by downloading the latest version of Pester. You can download Pester by running `Install-Module` in an elevated PowerShell console. The below command will force an install of the latest Pester version. Since the publisher [certificate](https://adamtheautomator.com/x509-certificates/) that Pester is signed with comes with Windows 10, we’ll need to use the `SkipPublisherCheck` parameter to install it.

```powershell
PS51> Install-Module Pester -Force -Verbose -SkipPublisherCheck
```

Once Pester is available, you can then run the script and dynamically create a set of Pester tests.

> _Note: You can also create Pester tests yourself without even using the PowerShell script I’ve provided._

The below PowerShell script will use Pester to generate an nUnit XML file from the output of the `$TestResults` variable defined in the [ADHealthCheck-NoResult.ps1](https://gist.github.com/AlexAsplund/28f6c3ef42418902885cde1b83ebc260) script.

Save this file as _Pester.ps1_ in the same folder as the AD health check script.

```powershell
# dot source in the ADHealthCheck file
. $PSScriptRoot\ADHealthCheck-NoResult.ps1
$Grouped = $TestResults | Group-Object Category

Foreach($Category in $Grouped) {
    Describe -Name $Category.Name -Tags ($Category.Group.Tags | Select -Unique) {
        Foreach($Result in $Category.Group){
            Context "$($Result.Source) - $($Result.TestName)" {
                It -Name "Should've passed" {
                    $Result.Pass | Should -Be -ExpectedValue $True -Because $Result.data
                }
            }
        }
    }
}
```

Finally, run `Invoke-Pester` below to invoke the _Pester.ps1_ file and save the results in `NUnitXml` format.

```powershell
PS51 > Invoke-Pester -Script @{Path = '.\Pester.ps1'} -OutputFile .\NunitReport.xml -OutputFormat NUnitXml
```

### Building an HTML Report with the Extent Tool

Once you have the NUnit XML file, you can now use this file to pass to a tool that can convert that to HTML. One of those tools is called [extent](https://github.com/extent-framework/extentreports-dotnet-cli). Extent is a handy tool for creating HTML reports from Nunit XML files.

First, [download extent](https://github.com/extent-framework/extentreports-dotnet-cli/raw/master/dist/extent.exe) to the same directory as the _NunitReport.xml_ file created earlier. Then execute the following commands in a PowerShell session. These commands will create the directory to store the HTML files and then execute _extent.exe_ to do the conversion.

```powershell
# Create report directory
PS51> mkdir .\HTMLReports

# Create the report
PS51> .\extent.exe -i .\NunitReport.xml -o .\HTMLReports\
```

When complete, you’ll find two HTML files in the _HTMLReports_ directory. These files will looks like the below screenshots you open them with a web browser.

![HTML report for Pester test output](/wp-content/uploads/2019/09/extent-dashboard.png)

HTML report for Pester test output

![HTML report for Pester test output](/wp-content/uploads/2019/09/extent-index.png)

HTML report for Pester test output

### Ingesting AD Health Check Results into PRTG

[PRTG](https://www.paessler.com/prtg) is a popular monitoring tool developed by Paessler that you can use to monitor your infrastructure and services. In this section, you’ll learn how to push the health check results to PRTG once the health check script has run.

Pushing results to PRTG does take more work rather than having the tool pull information but you’ll eventually see the setup is well worth the time spent.

#### Prerequisites

To successfully set up PRTG as a monitoring tool for the AD health check script built in this article, be sure you have:

*   PRTG installed and configured
*   All domain controllers set up in PRTG
*   The [_Send-AdhcResultToPrtg.ps1_](https://gist.github.com/AlexAsplund/83c8e374ed3d81d2de88767a4d9dc5e1) PowerShell script downloaded from GitHub
*   the URL and port of y0ur PRTG sensor

If you have each of the prereqs done, you can then follow the below step-by-step instructions on how I recommend to push these AD health check results to PRTG.

1.  Create a device in PRTG called _Domain_ or  whatever name you prefer.
2.  Create an _Advanced HTTP push sensor_ with an _IdentityToken_ of _directory-adhealthcheck_.  Note that this is case sensitive!
3.  For each domain controller device in PRTG, create one _Advanced HTTP push sensor_. For each IdentityToken, append each sensor with _\-adhealthcheck_ such as _dc01-adhealthcheck_.
4.  Add the contents of the _[Send-AdhcResultToPrtg.ps1](https://gist.github.com/AlexAsplund/83c8e374ed3d81d2de88767a4d9dc5e1)_ PowerShell script to the end of the [_ADHealthCheck-NoResult.ps1_](https://gist.github.com/AlexAsplund/28f6c3ef42418902885cde1b83ebc260) PowerShell script we’ve covered.
5.  Change the variable `$PRTGUrl` to the URL and port of your PRTG sensor.
6.  Execute the script.

Once complete, once the AD health check script completes, it should now push status to your PRTG sensors as shown below.

![PRTG sensors showing AD health](/wp-content/uploads/2019/09/active-directory-health-check-1.png "image_tooltip")

PRTG sensors showing AD health

## Scheduling the Active Directory Health Check Script

Monitoring AD health is an ongoing process. You should be running tests all the time rather than ad-hoc instances. Let’s schedule the Active Directory health check script to run at frequent intervals.

The easiest route of automating these checks is by adding the script to the task scheduler and letting it run under an AD user account or a [Group-Managed Service Account.](https://docs.microsoft.com/sv-se/windows-server/security/group-managed-service-accounts/group-managed-service-accounts-overview)

Using a Group Managed Service Account (gMSA) is the more secure way of executing scheduled tasks autonomously since only specified computer accounts that can fetch the password from AD. But some organizations may not have this luxury.

### Creating an AD User Account

Let’s first break down what it takes to get an AD user account set up to run the scheduled task.

If you’re going to be running a scheduled task as a user account, by all means, do not run it as your own account! Always create a separate user account for this purpose.

To save time time, you will see a PowerShell script below. This is an example script you can use to create an AD user account that is part of the _Domain Admins_ group. You can then use this account to run scheduled task with.

```powershell
# Change this to the OU of your service accounts
$OU = "OU=Service Accounts,DC=contoso,DC=com"
# Change this to the password that you want to use for the account
$Password = "JägareTvå"
$SecureString = $Password | ConvertTo-SecureString -AsPlainText -Force
New-ADUser -Enabled $True -Path $OU -Name svcADHealthCheck -AccountPassword $SecureString
# Restrict account to only Domain Controllers
$DomainControllers = (Get-ADDomainController -Filter *).Name

Set-ADAccount -Identity svcADHealthCheck -LogonWorkstations ($DomainControllers -Join ",")

# Making it Domain Admin (Sorry)
Add-ADGroupMember -Identity "Domain Admins" -Members svcADHealthCheck
```

### Creating a Group-Managed Service Account

Using a gMSA to run the health check is a bit more tricky if you aren’t using gMSA in your environment already, but it’s way more secure.

#### Creating a KDS Root Key

To create a gMSA account to run the AD health check script, first add a [KDS root key](https://docs.microsoft.com/sv-se/windows-server/security/group-managed-service-accounts/create-the-key-distribution-services-kds-root-key) if you don’t have one already.  You can check if you have a KDS root key by running the PowerShell command `Get-KDSRootKey` on a domain controller.

If you don’t have a KDS root key, you create one by running `Add-KDSRootKey -EffectiveImmediately` under a user account part of the Domain Admins AD group on a 2012R2 or later domain controller.

The key needs to replicate out to the other domain controllers to take full effect. More information on this process can be found in the [Microsoft documentation](https://docs.microsoft.com/sv-se/windows-server/security/group-managed-service-accounts/create-the-key-distribution-services-kds-root-key).

#### Creating the gMSA

Once the KDS root key is created, you’re ready to create the gMSA account with PowerShell. Below you can see an example script to use to create the gMSA account only allowed to authenticate from a domain controller in the _Domain Admins_ group.

```powershell
# Change to your domain
$Domain = "contoso.com"

$AccountName = "svcadhealthcheck"

# Create a GMSA called svcadhealthcheck (or in reality svcadhealthcheck$) and allow only Domain Controllers to fetch the password
New-ADServiceAccount $AccountName -DNSHostName "$AccountName.$Domain" –PrincipalsAllowedToRetrieveManagedPassword "Domain Controllers"

# Add the GMSA to Domain Admins
# Note that we're adding the the account by it's true SamAccountName 'svcadhealthcheck$'
Add-ADGroupMember -Identity "Domain Admins" -Members "$AccountName`$"
```

#### Installing and Testing the gMSA

Now the gMSA is created, the last step is to install and test it on all domain controllers. One way to do this is by using the [`Invoke-Command`](https://adamtheautomator.com/invoke-command/) PowerShell command. Below you can see a PowerShell script that will install the gMSA on all DCs and ensure it’s working properly.

```powershell
# This will run on all Domain Controllers
Invoke-Command -ComputerName (Get-ADDomainController -Filter *).Name -ScriptBlock {
    $Account = Get-ADServiceAccount -Filter { Name -eq 'svcadhealthcheck'}
    Install-ADServiceAccount $Account

    # Tests that the GMSA works on the computer
    # Returns $True if tests are OK
    $Test = Test-ADServiceAccount -Identity $Account.Name
    if($Test){
        Write-Output "GMSA test OK on $env:computername"
    }
    else {
        Write-Output "GMSA test FAILED on $env:computername"
    }

}
```

### Giving the gMSA Permission to Run as a Batch Job

Once the gMSA is installed, you’ll now need to give it permission to run as a batch job on the DCs. The account needs this right since it will be running autonomously in the background in a scheduled task.

You can set this permission via an existing GPO or by creating a new GPO and linking it to the _Domain Controllers_ OU. If you don’t already have a GPO to use, below are some steps you can take to create one.

1.  Launch the _[Group Policy](https://adamtheautomator.com/what-is-group-policy/) Editor_ on a DC.
2.  Right-click on the _Domain Controllers_ OU and select _Create GPO in this domain and link it here_.
3.  Name it _DC – Logon as batch_ or another name you prefer
4.  Right-click the GPO and click _Edit._
5.  Go to _Computer Configuration_ –> _Windows Settings_ –> _Security Settings_ –> _User Rights Assignment_.
6.  Left-click _Log on as batch job_ and click _Properties_.
7.  Click _Add user or Group._
8.  Click on _Object Types,_ select only _Service Accounts_ and click _OK._
9.  Search for the _svcADHealthCheck_ service account created earlier, select it and click _OK._

You should now see the gMSA under the list of AD objects like shown below.

![gMSA given rights to logon as a batch job](/wp-content/uploads/2019/09/log-on-as-batch-job.png "image_tooltip")

gMSA given rights to logon as a batch job

### Creating the Scheduled Task

Now that you have an account created to run the scheduled tasks, you can now create the scheduled task itself on a domain-joined server of your choice.

You can create the scheduled task via the GUI but that’s too much clicking! Instead, I recommend creating it with PowerShell. Why? Because you can simply copy the code you see below and be done with it.

Below you will find two scripts; both scripts are similar but one assumes an AD user account and the other assumes a gMSA. Be sure to use the appropriate script based on which account you’re using.

```powershell
# Replace with the path to your script
$ScriptPath = "C:\Scripts\ADHealthCheck.ps1"
# Replace with the username of the account that you created to run the task
$UserName = "svdADHealthCheck"

# Replace with the password that you set for above account
$Password = "JägareTvå!"
# Create the action that launches the script
$Action = New-ScheduledTaskAction -Execute "powershell.exe" -Argument "-ExecutionPolicy bypass -File '$ScriptPath'"
# Create the trigger that starts the task
$Trigger = New-ScheduledTaskTrigger -Once -At "12:00" -RepetitionInterval (New-TimeSpan -Hours 12)
# Create settings for the scheduled task
$Settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -StartWhenAvailable -RunOnlyIfNetworkAvailable -DontStopOnIdleEnd
# Create the scheduled task using a splat for readability
$Splat = @{
    User = "$env:USERDOMAIN\$UserName"
    Password = $Password
    TaskName = "ADHealthCheck"
    Action = $Action
    Trigger = $Trigger
    RunLevel = "Highest"
    Settings = $Settings
}
Register-ScheduledTask @Splat
```

```powershell
# Replace with the path to your script
$ScriptPath = "C:\Scripts\ADHealthCheck.ps1"
# Replace with the username of the account that you created to run the task
$UserName = "svdADHealthCheck$"
# Create the action that launches the script
$Action = New-ScheduledTaskAction -Execute "powershell.exe" -Argument "-ExecutionPolicy bypass -File '$ScriptPath'"
# Create the trigger that starts the task
$Trigger = New-ScheduledTaskTrigger -Once -At "12:00" -RepetitionInterval (New-TimeSpan -Hours 12)
# Create settings for the scheduled task
$Settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -StartWhenAvailable -RunOnlyIfNetworkAvailable -DontStopOnIdleEnd

# Create principal that defines the GMSA
$Principal = New-ScheduledTaskPrincipal -UserID "$env:USERDOMAIN\$UserName" -LogonType Password -RunLevel Highest
# Create the scheduled task using a splat for readability
$Splat = @{
    Principal = $Principal
    TaskName = "ADHealthCheck"
    Action = $Action
    Trigger = $Trigger
    RunLevel = "Highest"
    Settings = $Settings
}
Register-ScheduledTask @Splat
```

You’re done! At this time, the scheduled task will execute at the interval provided in one of the scripts above.

## Summary

Whew! If you have followed along all the way from Part I, you should know by now that AD health is a deep subject. There’s so much to this one topic that not even two lengthy blog posts could cover.

But, by now, you should have enough knowledge and a pre-built PowerShell framework that you can plug in other Active Directory health checks as they arise.

## Further Reading

*   [Some stuff you might not know about AD and Disaster Recovery](https://www.reddit.com/r/sysadmin/wiki/ms/activedirectorydisasterrecovery)
*   [What does DCDIAG actually… do?](https://docs.microsoft.com/en-us/archive/blogs/askds/what-does-dcdiag-actually-do)

Share this article

[Share on X](https://twitter.com/intent/tweet?url=https%3A%2F%2Fadamtheautomator.com%2Factive-directory-health-check-2%2F&text=Active%20Directory%20Health%20Check%20with%20PowerShell%20Framework)[Share on Facebook](https://www.facebook.com/sharer/sharer.php?u=https%3A%2F%2Fadamtheautomator.com%2Factive-directory-health-check-2%2F)[Share on LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fadamtheautomator.com%2Factive-directory-health-check-2%2F)

## Related Posts

![](https://adamtheautomator.com/wp-content/uploads/2024/11/image-23.png)

### [Managing Active Directory Groups with PowerShell: The Ultimate Guide](/powershell-ad-groups-guide/)

Learn how to manage Active Directory groups with PowerShell! This hands-on guide shows you how to query, create and modify AD groups using practical real-world

![](https://adamtheautomator.com/wp-content/uploads/2019/08/database-152091_1280.png)

### [Active Directory Database: PowerShell Monitoring Made Easy](/active-directory-database/)

Find the ntds.dit location and monitor your Active Directory database using PowerShell.

![](https://adamtheautomator.com/wp-content/uploads/2019/07/panic-1393619_1280.png)

### [How to Find Locked Out Users in Active Directory with PowerShell](/find-locked-out-users-in-active-directory-powershell/)

See what we can do to find locked out users in Active Directory with PowerShell!

## Categories

*   [IT Ops](/category/it-ops/)
*   [Cloud](/category/cloud/)
*   [DevOps](/category/devops/)
*   [Home Ops](/category/home-ops/)
*   [Information Security](/category/infosec/)
*   [Software Development](/category/software-development/)

## Site

*   [Home](/)
*   [Tutorials](/tutorials/)
*   [Instructors](/author/)
*   [Advertising](/advertising/)
*   [Recommended Resources](/resources/)
*   [About Adam](/about-adam/)

Copyright 2026© ATA Learning | [Privacy Policy](/privacy/)
