Skip to content

Instantly share code, notes, and snippets.

@irwins
Last active December 11, 2018 12:10
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save irwins/c08f9228a9abf9c6b2e81f03fc78ce8c to your computer and use it in GitHub Desktop.
Save irwins/c08f9228a9abf9c6b2e81f03fc78ce8c to your computer and use it in GitHub Desktop.
<#
Author: I.C.A. Strachan
Version: 1.1
Version History:
08-04-2016 1.0 - First Release
12-05-2016 1.1 - Fixed issues with Sitelinks & Subnets.
Purpose: Pester script to validate Active Directory configuration.
#>
[CmdletBinding()]
Param(
$xmlFile = 'ADReport-12052016.xml'
)
#region Active Directory configuration as you expect it to be. Modify to reflect your AD
$ADConfiguration = @{
Forest = @{
FQDN = 'pshirwin.local'
ForestMode = 'Windows2012R2Forest'
GlobalCatalogs = @(
'DC-DSC-01.pshirwin.local'
)
SchemaMaster = 'DC-DSC-01.pshirwin.local'
DomainNamingMaster = 'DC-DSC-01.pshirwin.local'
}
Domain = @{
NetBIOSName = 'PSHIRWIN'
DomainMode = 'Windows2012R2Domain'
RIDMaster = 'DC-DSC-01.pshirwin.local'
PDCEmulator = 'DC-DSC-01.pshirwin.local'
InfrastructureMaster = 'DC-DSC-01.pshirwin.local'
DistinguishedName = 'DC=pshirwin,DC=local'
DNSRoot = 'pshirwin.local'
DomainControllers = @('DC-DSC-01')
}
PasswordPolicy = @{
PasswordHistoryCount = 24
LockoutThreshold = 0
LockoutDuration = '00:30:00'
LockoutObservationWindow = '00:30:00'
MaxPasswordAge = '42.00:00:00'
MinPasswordAge = '1.00:00:00'
MinPasswordLength = 8
ComplexityEnabled = $true
}
Sites = @('Default-First-Site-Name','Branch01')
SiteLinks = @(
[PSCustomObject]@{
Name = 'DEFAULTIPSITELINK'
Cost = 100
ReplicationFrequencyInMinutes = 180
}
)
SubNets = @(
[PSCustomObject]@{
Name = '192.168.0.0/24'
Site = 'CN=Branch01,CN=Sites,CN=Configuration,DC=pshirwin,DC=local'
}
)
}
#endregion
#Import saved AD snapshot
$SavedADReport = Import-Clixml .\export\dsa\$xmlFile
Describe 'Active Directory configuration operational readiness' {
Context 'Verifying Forest Configuration'{
it "Forest FQDN $($ADConfiguration.Forest.FQDN)" {
$ADConfiguration.Forest.FQDN |
Should be $SavedADReport.ForestInformation.RootDomain
}
it "ForestMode $($ADConfiguration.Forest.ForestMode)"{
$ADConfiguration.Forest.ForestMode |
Should be $SavedADReport.ForestInformation.ForestMode.ToString()
}
}
Context 'Verifying GlobalCatalogs'{
$ADConfiguration.Forest.GlobalCatalogs |
ForEach-Object{
it "Server $($_) is a GlobalCatalog"{
$SavedADReport.ForestInformation.GlobalCatalogs.Contains($_) |
Should be $true
}
}
}
Context 'Verifying Domain Configuration'{
it "Total Domain Controllers $($ADConfiguration.Domain.DomainControllers.Count)" {
$ADConfiguration.Domain.DomainControllers.Count |
Should be @($SavedADReport.DomainControllers).Count
}
$ADConfiguration.Domain.DomainControllers |
ForEach-Object{
it "DomainController $($_) exists"{
$SavedADReport.DomainControllers.Name.Contains($_) |
Should be $true
}
}
it "DNSRoot $($ADConfiguration.Domain.DNSRoot)"{
$ADConfiguration.Domain.DNSRoot |
Should be $SavedADReport.DomainInformation.DNSRoot
}
it "NetBIOSName $($ADConfiguration.Domain.NetBIOSName)"{
$ADConfiguration.Domain.NetBIOSName |
Should be $SavedADReport.DomainInformation.NetBIOSName
}
it "DomainMode $($ADConfiguration.Domain.DomainMode)"{
$ADConfiguration.Domain.DomainMode |
Should be $SavedADReport.DomainInformation.DomainMode.ToString()
}
it "DistinguishedName $($ADConfiguration.Domain.DistinguishedName)"{
$ADConfiguration.Domain.DistinguishedName |
Should be $SavedADReport.DomainInformation.DistinguishedName
}
it "Server $($ADConfiguration.Domain.RIDMaster) is RIDMaster"{
$ADConfiguration.Domain.RIDMaster |
Should be $SavedADReport.DomainInformation.RIDMaster
}
it "Server $($ADConfiguration.Domain.PDCEmulator) is PDCEmulator"{
$ADConfiguration.Domain.PDCEmulator |
Should be $SavedADReport.DomainInformation.PDCEmulator
}
it "Server $($ADConfiguration.Domain.InfrastructureMaster) is InfrastructureMaster"{
$ADConfiguration.Domain.InfrastructureMaster |
Should be $SavedADReport.DomainInformation.InfrastructureMaster
}
}
Context 'Verifying Default Password Policy'{
it 'ComplexityEnabled'{
$ADConfiguration.PasswordPolicy.ComplexityEnabled |
Should be $SavedADReport.DefaultPassWordPoLicy.ComplexityEnabled
}
it 'Password History count'{
$ADConfiguration.PasswordPolicy.PasswordHistoryCount |
Should be $SavedADReport.DefaultPassWordPoLicy.PasswordHistoryCount
}
it "Lockout Threshold equals $($ADConfiguration.PasswordPolicy.LockoutThreshold)"{
$ADConfiguration.PasswordPolicy.LockoutThreshold |
Should be $SavedADReport.DefaultPassWordPoLicy.LockoutThreshold
}
it "Lockout duration equals $($ADConfiguration.PasswordPolicy.LockoutDuration)"{
$ADConfiguration.PasswordPolicy.LockoutDuration |
Should be $SavedADReport.DefaultPassWordPoLicy.LockoutDuration.ToString()
}
it "Lockout observation window equals $($ADConfiguration.PasswordPolicy.LockoutObservationWindow)"{
$ADConfiguration.PasswordPolicy.LockoutObservationWindow |
Should be $SavedADReport.DefaultPassWordPoLicy.LockoutObservationWindow.ToString()
}
it "Min password age equals $($ADConfiguration.PasswordPolicy.MinPasswordAge)"{
$ADConfiguration.PasswordPolicy.MinPasswordAge |
Should be $SavedADReport.DefaultPassWordPoLicy.MinPasswordAge.ToString()
}
it "Max password age equals $($ADConfiguration.PasswordPolicy.MaxPasswordAge)"{
$ADConfiguration.PasswordPolicy.MaxPasswordAge |
Should be $SavedADReport.DefaultPassWordPoLicy.MaxPasswordAge.ToString()
}
}
Context 'Verifying Active Directory Sites'{
$ADConfiguration.Sites |
ForEach-Object{
it "Site $($_)" {
$SavedADReport.Sites.Name.Contains($_) |
Should be $true
}
}
}
Context 'Verifying Active Directory Sitelinks'{
$lookupSiteLinks = $SavedADReport.Sitelinks | Group-Object -AsHashTable -Property Name
$ADConfiguration.Sitelinks |
ForEach-Object{
it "Sitelink $($_.Name)" {
$_.Name |
Should be $($lookupSiteLinks.$($_.Name).Name)
}
it "Sitelink $($_.Name) costs $($_.Cost)" {
$_.Cost |
Should be $lookupSiteLinks.$($_.Name).Cost
}
it "Sitelink $($_.Name) replication interval $($_.ReplicationFrequencyInMinutes)" {
$_.ReplicationFrequencyInMinutes |
Should be $lookupSiteLinks.$($_.Name).ReplicationFrequencyInMinutes
}
}
}
Context 'Verifying Active Directory Subnets'{
$lookupSubnets = $SavedADReport.SubNets | Group-Object -AsHashTable -Property Name
$ADConfiguration.Subnets |
ForEach-Object{
it "Subnet $($_.Name)" {
$_.Name |
Should be $lookupSubnets.$($_.Name).Name
}
it "Site $($_.Site)" {
$_.Site |
Should be $lookupSubnets.$($_.Name).Site
}
}
}
}
@micmaher
Copy link

Hi Irwin,

Love the script.

Just a heads up for others. The domain controller names in the ADConfiguration Hash table are case sensitive.

Should the case mismatch this error will show.

Context Verifying Domain Configuration
[+] Total Domain Controllers 5 131ms
[-] DomainController xxxvmads001 exists 19ms
Expected: {True}
But was: {False}
103: $SavedADReport.DomainControllers.Name.Contains($_) | Should be $true
at , C:\Scripts\pesterTest.ps1: line 103

The giveaway was that the DC name xxxvmads001 in the error above doesn't match the variable below.

PS C:\Scripts> $SavedADReport.DomainControllers.Name
XXXVMADS001

I've never looked at Pester until today so I am not sure if a case insensitive comparison can be made.

Regards,

Michael

@irwins
Copy link
Author

irwins commented Apr 24, 2016

Thanks for the headsup! There's a discussion on twitter right now on creating an OVF (Operation Validation Framework). Will be interesting to see how it develops! :-)

@irwins
Copy link
Author

irwins commented May 12, 2016

Hi All,

Just updated the script. Found some issues with the Sitelink & subnets test. I'll be sure to update any other known issues...

Rg./Irwin

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