ADAudit.psm1

Function Export-ADConfig {
    <#
        .SYNOPSIS
            Exports the configuration of an Active Directory forest to an XML file.
 
        .DESCRIPTION
            The Export-ADConfig cmdlet retrieves the AD configuration via the Get-ADConfig cmdlet
            and then exports it to an XML file. If the -AsGoldConfig switch is used, the file is
            prefixed GoldConfig-, otherwise the prefix is ADConfig-.
 
        .PARAMETER InputObject
            The AD configuration to export. If not provided Get-ADConfig is executed to retrieve the current
            config.
 
        .PARAMETER Path
            Specify the path for the export file. If not specified, the file is either ADConfig-<currentdate>.xml
            or GoldConfig-<currentdate>.xml depending on whether -AsgoldConfig is specified.
 
        .PARAMETER AsGoldConfig
            Use to export to a file named GoldConfig-<currentdate>.xml.
 
        .EXAMPLE
            Export-ADConfig
 
            Retrieves the current AD configuration and exports it to a file named ADReport-<date>.xml.
 
        .EXAMPLE
            Export-ADConfig -AsGoldConfig
 
            Retrieves the current AD configuration and exports it to a file named GoldConfig-<date>.xml.
 
        .EXAMPLE
            Get-ADConfig | Export-ADConfig -AsGoldConfig
 
            Exports a previously retrieved AD configuration to a file named GoldConfig-<date>.xml.
    #>

    [cmdletbinding(DefaultParameterSetName='Default')]
    Param(
        [Parameter(ParameterSetName='Path')]
        [Parameter(ParameterSetName='AsGoldConfig')]
        [Parameter(ParameterSetName='Default')]
        [Parameter(Position = 0, ValueFromPipeline)]
        [object]
        $InputObject = (Get-ADConfig),

        [Parameter(ParameterSetName='Path')]
        [string]
        $Path,

        [Parameter(ParameterSetName='AsGoldConfig')]
        [switch]
        $AsGoldConfig
    )
    Begin {
        If ($AsGoldConfig) {
            $ReportPrefix = 'GoldConfig'
        }
        Else {
            $ReportPrefix = 'ADReport'
        }

        if (-not $Path) { $Path = "$ReportPrefix-$(Get-Date -format yyyy-MM-dd).xml" }
    }
    Process {
        $InputObject | Export-Clixml -Path $Path -Encoding UTF8
    }
}
Function Get-ADConfig {
    <#
        .SYNOPSIS
            Retrieves the configuration of an Active Directory forest.
 
        .DESCRIPTION
            The Get-ADConfig cmdlet retrieves various configuration information about Active Directory
            and returns that information as a PowerShell ojbect. It uses cmdlets such as: Get-ADRootDSE,
            Get-ADForest, Get-ADDomain, Get-ADDomainController, Get-ADTrust etc. to gather information.
 
            The primary purpose of this tool is to gather a single and detailed snapshot of the configuration
            of an Active Directory forest to use to validate the future health of that forest.
 
        .EXAMPLE
            Get-ADConfig
 
            Retrieves the current configuration of Active Directory and returns it as a PowerShell object.
    #>

    [cmdletbinding()]
    Param()

    $ADDomain = Get-ADDomain

    #HashTable to save ADReport
    [pscustomobject]@{
        RootDSE                   = (Get-ADRootDSE)
        ForestInformation         = (Get-ADForest)
        DomainInformation         = $ADDomain
        DomainControllers         = (Get-ADDomainController -Filter *)
        DomainTrusts              = (Get-ADTrust -Filter *)
        DefaultPassWordPoLicy     = (Get-ADDefaultDomainPasswordPolicy)
        AuthenticationPolicies    = (Get-ADAuthenticationPolicy -LDAPFilter '(name=AuthenticationPolicy*)')
        AuthenticationPolicySilos = (Get-ADAuthenticationPolicySilo -Filter 'Name -like "*AuthenticationPolicySilo*"')
        CentralAccessPolicies     = (Get-ADCentralAccessPolicy -Filter *)
        CentralAccessRules        = (Get-ADCentralAccessRule -Filter *)
        ClaimTransformPolicies    = (Get-ADClaimTransformPolicy -Filter *)
        ClaimTypes                = (Get-ADClaimType -Filter *)
        DomainAdministrators      = (Get-ADGroup -Identity $('{0}-512' -f $ADDomain.domainSID) | Get-ADGroupMember -Recursive)
        OrganizationalUnits       = (Get-ADOrganizationalUnit -Filter *)
        OptionalFeatures          = (Get-ADOptionalFeature -Filter *)
        Sites                     = (Get-ADReplicationSite -Filter *)
        Subnets                   = (Get-ADReplicationSubnet -Filter *)
        SiteLinks                 = (Get-ADReplicationSiteLink -Filter *)
        LDAPDNS                   = (Resolve-DnsName -Name "_ldap._tcp.$($ADDomain.DNSRoot)" -Type srv) | Sort-Object nametarget, name, type
        KerberosDNS               = (Resolve-DnsName -Name "_kerberos._tcp.$($ADDomain.DNSRoot)" -Type srv) | Sort-Object nametarget, name, type
    }
}
Function Test-ActiveDirectory {
    <#
        .SYNOPSIS
            Runs Pester tests to validate whether the Active Directory configuration and health matches
            the previously recorded known-good state of the configuration.
 
        .DESCRIPTION
            ..
 
        .PARAMETER ADSnapshotFile
            Optional: The path to a snapshot of Active Directory that you want to validate against the Gold config.
            If not provided, a current config is retrieved at runtime via the Get-ADConfig cmdlet.
 
        .PARAMETER ADGoldFile
            The path to the 'gold' known-good snapshot of Active Directory that you want to validate against.
            The cmdlet looks for a file named ADGoldConfig-*.xml in the current directory
 
        .PARAMETER Tag
            Optional: Only run checks with one of these Pester tags (e.g. 'Forest','Domain','Password',
            'Sites','Subnets','Sitelinks','ADHC'). If not specified, all checks are run.
 
        .PARAMETER ExcludeTag
            Optional: Skip checks with one of these Pester tags. For example, use -ExcludeTag ADHC to skip
            the live health checks (NLTest/DCDiag/RepAdmin/ports/services/DNS) and only compare configuration.
 
        .EXAMPLE
            Test-ActiveDirectory
 
            Compares the current Active Directory configuration against the most recent GoldConfig-*.xml
            file found in the current directory and reports any differences.
 
        .EXAMPLE
            Test-ActiveDirectory -ExcludeTag ADHC
 
            Compares configuration only, skipping the live health checks (useful when running from a host
            that isn't a domain member/controller, or doesn't have the AD administrative tools installed).
    #>

    [CmdletBinding()]
    Param(
        [string]
        $ADSnapshotFile,

        [string]
        $ADGoldFile = (Get-ChildItem (Join-Path $Pwd 'GoldConfig-*.xml') | Select-Object -Last 1).fullname,

        [string[]]
        $Tag,

        [string[]]
        $ExcludeTag
    )
    $Container = New-PesterContainer -Path (Join-Path $PSScriptRoot '../ActiveDirectory.Checks.ps1') -Data @{
        ADSnapshotFile = $ADSnapshotFile
        ADGoldFile     = $ADGoldFile
    }

    $PesterConfig = New-PesterConfiguration
    $PesterConfig.Run.Container = $Container
    $PesterConfig.Run.PassThru = $true

    # Pester v6 fails a -ForEach that resolves to $null/empty unless the It also carries
    # -AllowNullOrEmptyForEach. ActiveDirectory.Checks.ps1 deliberately doesn't use that switch, so it
    # can also run unmodified on Pester v5 (where the switch doesn't exist). Relaxing the same behaviour
    # here, at the run configuration level, covers Pester v6 instead -- guarded, since this property
    # doesn't exist on Pester v5's configuration object and setting it there would throw.
    if ($PesterConfig.Run.PSObject.Properties.Name -contains 'FailOnNullOrEmptyForEach') {
        $PesterConfig.Run.FailOnNullOrEmptyForEach = $false
    }

    if ($Tag) { $PesterConfig.Filter.Tag = $Tag }
    if ($ExcludeTag) { $PesterConfig.Filter.ExcludeTag = $ExcludeTag }

    Invoke-Pester -Configuration $PesterConfig
}