Private/Helpers/Invoke-ADRetrievalWithProgress.ps1

function Invoke-ADRetrievalWithProgress {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [ValidateSet("Users", "Computers", "Groups", "ForestInfo", "Sites", "Trusts", "Policies", "OrganizationalUnits", "DomainInfo", "DomainControllers")]
        [string]$ObjectType,

        [Parameter()]
        [string]$Filter = "*", # Default filter

        [Parameter()]
        [string[]]$Properties, # Properties to retrieve

        [Parameter(Mandatory)]
        [scriptblock]$ProcessingScript, # Transformation logic for each object

        [string]$ActivityName = "Retrieving $ObjectType"
    )

    # Define the capabilities for each ObjectType
    $objectTypeCapabilities = @{
        "Users"               = @{ Cmdlet = "Get-ADUser"; SupportsFilter = $true; SupportsProperties = $true }
        "Computers"           = @{ Cmdlet = "Get-ADComputer"; SupportsFilter = $true; SupportsProperties = $true }
        "Groups"              = @{ Cmdlet = "Get-ADGroup"; SupportsFilter = $true; SupportsProperties = $true }
        "ForestInfo"          = @{ Cmdlet = "Get-ADForest"; SupportsFilter = $false; SupportsProperties = $false }
        "Sites"               = @{ Cmdlet = "Get-ADReplicationSite"; SupportsFilter = $true; SupportsProperties = $true }
        "Trusts"              = @{ Cmdlet = "Get-ADTrust"; SupportsFilter = $true; SupportsProperties = $false }
        "Policies"            = @{ Cmdlet = "Get-GPO"; SupportsFilter = $false; SupportsProperties = $false }
        "OrganizationalUnits" = @{ Cmdlet = "Get-ADOrganizationalUnit"; SupportsFilter = $true; SupportsProperties = $true }
        "DomainInfo"          = @{ Cmdlet = "Get-ADDomain"; SupportsFilter = $false; SupportsProperties = $false }
        "DomainControllers"   = @{ Cmdlet = "Get-ADDomainController"; SupportsFilter = $true; SupportsProperties = $false }
    }

    # Validate ObjectType and retrieve capabilities
    if (-not $objectTypeCapabilities.ContainsKey($ObjectType)) {
        Write-Log "Unsupported ObjectType: $ObjectType" -Level Error
        throw "Unsupported ObjectType: $ObjectType"
    }

    $cmdletInfo = $objectTypeCapabilities[$ObjectType]
    $cmdletName = $cmdletInfo.Cmdlet
    $supportsFilter = $cmdletInfo.SupportsFilter
    $supportsProperties = $cmdletInfo.SupportsProperties

    # Ensure that centralized credentials are set
    if (-not $script:adminCreds) {
        Write-Log "Admin credentials not found. Please run Get-DomainReport first." -Level Error
        return $null
    }
    else {
        $Credential = $script:adminCreds
    }

    try {
        Write-Log "Starting retrieval of $ObjectType..." -Level Info

        # Prepare count parameters
        $countParams = @{ Credential = $Credential }
        if ($supportsFilter) {
            $countParams['Filter'] = $Filter
        }

        # Handle single-object cmdlets like Get-ADForest, Get-GPO, Get-ADDomain
        if ($ObjectType -in @("ForestInfo", "Policies", "DomainInfo")) {
            $total = 1
        }
        else {
            # Attempt to retrieve objects to count them
            try {
                $total = (& $cmdletName @countParams | Measure-Object).Count
            }
            catch {
                Write-Log "Error counting objects for ${ObjectType}: $($_.Exception.Message)" -Level Error
                return $null
            }
        }

        if ($total -eq 0) {
            Write-Log "No $ObjectType found based on the specified criteria." -Level Warning
            return @()
        }

        Write-Log "Retrieving and processing $total $ObjectType..." -Level Info

        # Build retrieval parameters
        $getParams = @{ Credential = $Credential }
        if ($supportsFilter) {
            $getParams['Filter'] = $Filter
        }
        if ($supportsProperties -and $Properties) {
            $getParams['Properties'] = $Properties
        }

        $count = 0
        $results = @()

        $objects = try {
            & $cmdletName @getParams
        }
        catch {
            Write-Log "Error retrieving ${ObjectType}: $($_.Exception.Message)" -Level Error
            return $null
        }

        foreach ($item in $objects) {
            $count++
            try {
                # Apply the transformation logic provided by the caller
                $obj = & $ProcessingScript $item

                # Update progress using Show-ProgressHelper
                $percent = [int]( ($count / $total) * 100 )
                Show-ProgressHelper -Activity $ActivityName -Status "Processing $ObjectType $count of $total" -PercentComplete $percent

                # Collect the transformed object
                $results += $obj
            }
            catch {
                Write-Log "Error processing ${ObjectType}: $($_.Exception.Message)" -Level Warning

                # Create a fallback object in case of processing error
                switch ($ObjectType) {
                    "Users" {
                        $errorObject = [PSCustomObject]@{
                            SamAccountName       = $item.SamAccountName
                            DisplayName          = $null
                            EmailAddress         = $null
                            Enabled              = $null
                            LastLogonDate        = $null
                            PasswordLastSet      = $null
                            PasswordNeverExpires = $null
                            PasswordExpired      = $null
                            DistinguishedName    = $item.DistinguishedName
                            MemberOf             = @()
                            AccountStatus        = "Error"
                            AccessStatus         = "Access Error: $($_.Exception.Message)"
                        }
                        Add-Member -InputObject $errorObject -MemberType ScriptMethod -Name "ToString" -Value {
                            "SamAccountName=$($this.SamAccountName); Status=Error; Groups=0"
                        } -Force
                        $results += $errorObject
                    }
                    "Computers" {
                        $errorObject = [PSCustomObject]@{
                            Name                   = $item.Name
                            IPv4Address            = $null
                            DNSHostName            = $null
                            OperatingSystem        = $null
                            OperatingSystemVersion = $null
                            Enabled                = $null
                            LastLogonDate          = $null
                            Created                = $null
                            Modified               = $null
                            DistinguishedName      = $item.DistinguishedName
                            ServicePrincipalNames  = $null
                            MemberOf               = @()
                            AccessStatus           = "Access Error: $($_.Exception.Message)"
                            NetworkStatus          = "Error"
                            IsAlive                = $false
                        }
                        Add-Member -InputObject $errorObject -MemberType ScriptMethod -Name "ToString" -Value {
                            "Name=$($this.Name); NetworkStatus=Error; IsAlive=$($this.IsAlive); Groups=0"
                        } -Force
                        $results += $errorObject
                    }
                    "Groups" {
                        $errorObject = [PSCustomObject]@{
                            Name                   = $item.Name
                            Description            = $item.Description
                            GroupCategory          = $item.GroupCategory
                            GroupScope             = $item.GroupScope
                            TotalNestedMemberCount = 0
                            Members                = @()
                            Created                = $item.Created
                            Modified               = $item.Modified
                            DistinguishedName      = $item.DistinguishedName
                            AccessStatus           = "Access Error: $($_.Exception.Message)"
                        }
                        Add-Member -InputObject $errorObject -MemberType ScriptMethod -Name "ToString" -Value {
                            "Name=$($this.Name); Status=Error"
                        } -Force
                        $results += $errorObject
                    }
                    "ForestInfo" {
                        Write-Log "Error retrieving Forest Info: $($_.Exception.Message)" -Level Warning
                        $results += $null
                    }
                    "Sites" {
                        Write-Log "Error retrieving Site Info: $($_.Exception.Message)" -Level Warning
                        $results += $null
                    }
                    "Trusts" {
                        Write-Log "Error retrieving Trust Info: $($_.Exception.Message)" -Level Warning
                        $results += $null
                    }
                    "Policies" {
                        Write-Log "Error retrieving GPOs: $($_.Exception.Message)" -Level Warning
                        $results += $null
                    }
                    "OrganizationalUnits" {
                        Write-Log "Error retrieving Organizational Units: $($_.Exception.Message)" -Level Warning
                        $results += $null
                    }
                    "DomainInfo" {
                        Write-Log "Error retrieving Domain Info: $($_.Exception.Message)" -Level Warning
                        $results += $null
                    }
                    "DomainControllers" {
                        Write-Log "Error retrieving Domain Controllers: $($_.Exception.Message)" -Level Warning
                        $results += $null
                    }
                    default {
                        Write-Log "Unhandled ObjectType: $ObjectType" -Level Warning
                        $results += $null
                    }
                }
            }
        }

        # Finalize progress using Show-ProgressHelper
        Show-ProgressHelper -Activity $ActivityName -Completed

        Write-Log "Successfully retrieved $($results.Count) $ObjectType." -Level Info
        return $results
    }
    catch {
        Write-Log "Failed to retrieve ${ObjectType}: $($_.Exception.Message)" -Level Error
    }
}