functions/gplinks/Test-DMGPLink.ps1

function Test-DMGPLink
{
    <#
    .SYNOPSIS
        Tests, whether the configured group policy linking matches the desired state.
     
    .DESCRIPTION
        Tests, whether the configured group policy linking matches the desired state.
        Define the desired state using the Register-DMGPLink command.
     
    .PARAMETER Server
        The server / domain to work with.
     
    .PARAMETER Credential
        The credentials to use for this operation.
     
    .EXAMPLE
        PS C:\> Test-DMGPLink -Server contoso.com
 
        Tests, whether the group policy links of contoso.com match the configured state
    #>

    [CmdletBinding()]
    param (
        [PSFComputer]
        $Server,
        
        [PSCredential]
        $Credential
    )
    
    begin
    {
        $parameters = $PSBoundParameters | ConvertTo-PSFHashtable -Include Server, Credential
        $parameters['Debug'] = $false
        Assert-ADConnection @parameters -Cmdlet $PSCmdlet
        Invoke-Callback @parameters -Cmdlet $PSCmdlet
        Assert-Configuration -Type GroupPolicyLinks -Cmdlet $PSCmdlet
        Set-DMDomainContext @parameters
    }
    process
    {
        $gpoDisplayToDN = @{ }
        $gpoDNToDisplay = @{ }
        foreach ($adPolicyObject in (Get-ADObject @parameters -LDAPFilter '(objectCategory=groupPolicyContainer)' -Properties DisplayName, DistinguishedName)) {
            $gpoDisplayToDN[$adPolicyObject.DisplayName] = $adPolicyObject.DistinguishedName
            $gpoDNToDisplay[$adPolicyObject.DistinguishedName] = $adPolicyObject.DisplayName
        }

        #region Process Configuration
        foreach ($organizationalUnit in $script:groupPolicyLinks.Keys) {
            $resolvedName = Resolve-String -Text $organizationalUnit
            $desiredState = $script:groupPolicyLinks[$organizationalUnit].Values

            $resultDefaults = @{
                Server = $Server
                ObjectType = 'GPLink'
                Identity = $resolvedName
                Configuration = $desiredState
            }
            
            try {
                $adObject = Get-ADObject @parameters -Identity $resolvedName -ErrorAction Stop -Properties gPLink, Name, DistinguishedName
                $resultDefaults['ADObject'] = $adObject
            }
            catch {
                Write-PSFMessage -String 'Test-DMGPLink.OUNotFound' -StringValues $resolvedName -ErrorRecord $_ -Tag 'panic','failed'
                New-TestResult @resultDefaults -Type 'MissingParent'
                Continue
            }

            $currentState = $adObject | ConvertTo-GPLink -PolicyMapping $gpoDNToDisplay
            Add-Member -InputObject $adObject -MemberType NoteProperty -Name LinkedGroupPolicyObjects -Value $currentState -Force
            if (-not $currentState) {
                New-TestResult @resultDefaults -Type 'New'
                    continue
            }

            $currentStateFilteredSorted = $currentState | Where-Object Status -ne 'Disabled' | Sort-Object Precedence
            $currentStateSorted = $currentState | Sort-Object Precedence
            $desiredStateSorted = $desiredState | Sort-Object Precedence

            if (Compare-Array -ReferenceObject $currentStateFilteredSorted.DisplayName -DifferenceObject ($desiredStateSorted.PolicyName | Resolve-String) -Quiet -OrderSpecific) {
                if (Compare-Array -ReferenceObject $currentStateSorted.DisplayName -DifferenceObject ($desiredStateSorted.PolicyName | Resolve-String) -Quiet -OrderSpecific) { continue }
                else {
                    New-TestResult @resultDefaults -Type 'UpdateDisabledOnly' -Changed ($currentStateSorted | Where-Object DisplayName -notin $desiredStateSorted.PolicyName)
                    continue
                }
            }

            if ($currentStateSorted | Where-Object Status -eq 'Disabled') {
                New-TestResult @resultDefaults -Type 'UpdateSomeDisabled' -Changed ($currentStateSorted | Where-Object DisplayName -notin $desiredStateSorted.PolicyName)
                continue
            }

            New-TestResult @resultDefaults -Type 'Update' -Changed ($currentStateSorted | Where-Object DisplayName -notin $desiredStateSorted.PolicyName)
        }
        #endregion Process Configuration

        #region Process Managed Estate
        # OneLevel needs to be converted to base, as searching for OUs with "OneLevel" would return unmanaged OUs.
        # This search however is targeted at GPOs linked to managed OUs only.
        $translateScope = @{
            'Subtree' = 'Subtree'
            'OneLevel' = 'Base'
            'Base' = 'Base'
        }
        $configuredContainers = $script:groupPolicyLinks.Keys | Resolve-String
        $adObjects = foreach ($searchBase in (Resolve-ContentSearchBase @parameters)) {
            Get-ADObject @parameters -LDAPFilter '(gPLink=*)' -SearchBase $searchBase.SearchBase -SearchScope $translateScope[$searchBase.SearchScope] -Properties gPLink, Name, DistinguishedName
        }

        foreach ($adObject in $adObjects) {
            # If we have a configuration on it, it has already been processed
            if ($adObject.DistinguishedName -in $configuredContainers) { continue }
            if ([string]::IsNullOrWhiteSpace($adObject.GPLink)) { continue }

            $linkObjects = $adObject | ConvertTo-GPLink -PolicyMapping $gpoDNToDisplay
            Add-Member -InputObject $adObject -MemberType NoteProperty -Name LinkedGroupPolicyObjects -Value $linkObjects -Force
            if (-not ($linkObjects | Where-Object Status -eq Enabled)) {
                New-TestResult -ObjectType GPLink -Type 'DeleteDisabledOnly' -Identity $adObject.DistinguishedName -Server $Server -ADObject $adObject
                continue
            }
            elseif (-not ($linkObjects | Where-Object Status -eq Disabled)) {
                New-TestResult -ObjectType GPLink -Type 'Delete' -Identity $adObject.DistinguishedName -Server $Server -ADObject $adObject
                continue
            }
            New-TestResult -ObjectType GPLink -Type 'DeleteSomeDisabled' -Identity $adObject.DistinguishedName -Server $Server -ADObject $adObject
        }
        #endregion Process Managed Estate
    }
}