Public/DomainReport/Security/Get-ADSecurityConfiguration.ps1

function Get-ADSecurityConfiguration {
    try {
        Write-Log "Retrieving AD security configuration..." -Level Info

        # Retrieve Organizational Units (OUs)
        $OUs = Invoke-ADRetrievalWithProgress -ObjectType $script:ObjectType.OrganizationalUnits `
            -Filter "*" `
            -Properties @('Name', 'DistinguishedName') `
            -ProcessingScript {
            param($ou)

            [PSCustomObject]@{
                Name              = $ou.Name
                DistinguishedName = $ou.DistinguishedName
                Description       = $ou.Description
                Created           = $ou.Created
                Modified          = $ou.Modified
            }
        } `
            -ActivityName "Retrieving Organizational Units"

        if (-not $OUs) {
            Write-Log "Failed to retrieve Organizational Units." -Level Warning
            $OUs = @()
        }

        # Retrieve Domain Controllers (DCs)
        $DCs = Invoke-ADRetrievalWithProgress -ObjectType $script:ObjectType.DomainControllers `
            -Filter "*" `
            -ProcessingScript {
            param($dc)

            [PSCustomObject]@{
                HostName = $dc.HostName
            }
        } `
            -ActivityName "Retrieving Domain Controllers"

        if (-not $DCs) {
            Write-Log "Failed to retrieve Domain Controllers." -Level Warning
            $DCs = @()
        }

        # Compile the security configuration into a PSCustomObject
        $securityConfig = [PSCustomObject]@{
            ObjectACLs    = Get-CriticalObjectACLs -OUs $OUs
            FileShareACLs = Get-CriticalShareACLs -DCs $DCs
        }

        # Add ToString method to securityConfig
        Add-Member -InputObject $securityConfig -MemberType ScriptMethod -Name "ToString" -Value {
            "ObjectACLs=$($this.ObjectACLs.Count); FileShareACLs=$($this.FileShareACLs.Count); SPNConfiguration=$($this.SPNConfiguration.Count)"
        } -Force

        Write-Log "Successfully retrieved AD security configuration." -Level Info

        return $securityConfig
    }
    catch {
        Write-Log "Error retrieving AD security configuration: $($_.Exception.Message)" -Level Error
        return $null
    }
}

function Get-CriticalObjectACLs {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [array]$OUs
    )

    try {
        Write-Log "Collecting ACLs for critical AD objects..." -Level Info

        # Define a processing scriptblock for each OU
        $processingScript = {
            param($ou)

            try {
                # Retrieve ACL for the OU
                $acl = Get-Acl -Path ("AD:" + $ou.DistinguishedName)

                # Process Access Rules
                $accessRules = $acl.Access | ForEach-Object {
                    [PSCustomObject]@{
                        Principal  = $_.IdentityReference.Value
                        AccessType = $_.AccessControlType.ToString()
                        Rights     = $_.ActiveDirectoryRights.ToString()
                        Inherited  = $_.IsInherited
                    }
                }

                # Construct the ACL object
                [PSCustomObject]@{
                    OU          = $ou.Name
                    Path        = $ou.DistinguishedName
                    Owner       = $acl.Owner
                    AccessRules = $accessRules
                }
            }
            catch {
                Write-Log "Error getting ACL for $($ou.DistinguishedName): $($_.Exception.Message)" -Level Warning
                return $null
            }
        }

        # Use the helper to process each OU with progress reporting
        $objectACLs = Invoke-ADRetrievalWithProgress -ObjectType "OrganizationalUnits" `
            -Filter "*" `
            -Properties @('Name', 'DistinguishedName') `
            -InputData $OUs `
            -ProcessingScript $processingScript `
            -ActivityName "Retrieving OU ACLs"

        # Remove any null entries resulting from errors
        $objectACLs = $objectACLs | Where-Object { $_ -ne $null }

        # Add ToString method to each ACL object
        foreach ($aclObj in $objectACLs) {
            Add-Member -InputObject $aclObj -MemberType ScriptMethod -Name "ToString" -Value {
                "OU=$($this.OU); Owner=$($this.Owner); Rules=$($this.AccessRules.Count)"
            } -Force
        }

        Write-Log "Successfully collected ACLs for critical AD objects." -Level Info

        return $objectACLs
    }
    catch {
        Write-Log "Error collecting critical object ACLs: $($_.Exception.Message)" -Level Error
        return @()
    }
}


function Get-CriticalShareACLs {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [array]$DCs
    )

    try {
        Write-Log "Collecting ACLs for SYSVOL and NETLOGON shares..." -Level Info

        # Define the shares to retrieve
        $shares = @("SYSVOL", "NETLOGON")

        # Prepare share information from all DCs
        $shareInfos = foreach ($dc in $DCs) {
            foreach ($share in $shares) {
                [PSCustomObject]@{
                    HostName  = $dc.HostName
                    ShareName = $share
                }
            }
        }

        # Define a processing scriptblock for each share
        $processingScript = {
            param($shareInfo)

            try {
                # Construct the share path
                $path = "\\$($shareInfo.HostName)\$($shareInfo.ShareName)"

                # Retrieve ACL for the share
                $acl = Get-Acl -Path $path

                # Process Access Rules
                $accessRules = $acl.Access | ForEach-Object {
                    [PSCustomObject]@{
                        Principal  = $_.IdentityReference.Value
                        AccessType = $_.AccessControlType.ToString()
                        Rights     = $_.FileSystemRights.ToString()
                        Inherited  = $_.IsInherited
                    }
                }

                # Construct the Share ACL object
                [PSCustomObject]@{
                    ShareName   = $shareInfo.ShareName
                    Path        = $path
                    Owner       = $acl.Owner
                    AccessRules = $accessRules
                }
            }
            catch {
                Write-Log "Error getting ACL for $($shareInfo.ShareName): $($_.Exception.Message)" -Level Warning
                return $null
            }
        }

        # Use the helper to process each share with progress reporting
        $shareACLs = Invoke-ADRetrievalWithProgress -ObjectType "CustomShares" `
            -ProcessingScript $processingScript `
            -ActivityName "Retrieving Share ACLs" `
            -InputData $shareInfos

        # Remove any null entries resulting from errors
        $shareACLs = $shareACLs | Where-Object { $_ -ne $null }

        # Add ToString method to each Share ACL object
        foreach ($shareAclObj in $shareACLs) {
            Add-Member -InputObject $shareAclObj -MemberType ScriptMethod -Name "ToString" -Value {
                "Share=$($this.ShareName); Owner=$($this.Owner); Rules=$($this.AccessRules.Count)"
            } -Force
        }

        Write-Log "Successfully collected ACLs for SYSVOL and NETLOGON shares." -Level Info

        return $shareACLs
    }
    catch {
        Write-Log "Error collecting share ACLs: $($_.Exception.Message)" -Level Error
        return @()
    }
}