Private/Helpers/Invoke-ADRetrievalWithProgress.ps1

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

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

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

        [Parameter()]
        [array]$InputData, # Optional input data to process instead of retrieving via cmdlet

        [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; SupportsCredential = $true }
        "Computers"           = @{ Cmdlet = "Get-ADComputer"; SupportsFilter = $true; SupportsProperties = $true; SupportsCredential = $true }
        "Groups"              = @{ Cmdlet = "Get-ADGroup"; SupportsFilter = $true; SupportsProperties = $true; SupportsCredential = $true }
        "ForestInfo"          = @{ Cmdlet = "Get-ADForest"; SupportsFilter = $false; SupportsProperties = $false; SupportsCredential = $true }
        "Sites"               = @{ Cmdlet = "Get-ADReplicationSite"; SupportsFilter = $true; SupportsProperties = $true; SupportsCredential = $true }
        "Trusts"              = @{ Cmdlet = "Get-ADTrust"; SupportsFilter = $true; SupportsProperties = $false; SupportsCredential = $true }
        "Policies"            = @{ Cmdlet = "Get-GPO"; SupportsFilter = $false; SupportsProperties = $false; SupportsCredential = $false } 
        "OrganizationalUnits" = @{ Cmdlet = "Get-ADOrganizationalUnit"; SupportsFilter = $true; SupportsProperties = $true; SupportsCredential = $true }
        "DomainInfo"          = @{ Cmdlet = "Get-ADDomain"; SupportsFilter = $false; SupportsProperties = $false; SupportsCredential = $true }
        "DomainControllers"   = @{ Cmdlet = "Get-ADDomainController"; SupportsFilter = $true; SupportsProperties = $false; SupportsCredential = $true }
        "CustomShares"        = @{ Cmdlet = $null; SupportsFilter = $false; SupportsProperties = $false; SupportsCredential = $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
    $supportsCredential = $cmdletInfo.SupportsCredential

    # Ensure that centralized credentials are set, unless ObjectType does not support credentials
    if ($supportsCredential -and -not $script:adminCreds) {
        Write-Log "Admin credentials not found. Please run Get-DomainReport first." -Level Error
        return $null
    }
    elseif ($supportsCredential) {
        $Credential = $script:adminCreds
    }

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

        if ($InputData) {
            # Processing pre-fetched data
            $total = $InputData.Count
            if ($total -eq 0) {
                Write-Log "No $ObjectType provided in InputData." -Level Warning
                return @()
            }
        }
        else {
            if ($cmdletName) {
                # Prepare count parameters
                $countParams = @{ }
                if ($supportsCredential) {
                    $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 @()
                }
            }
            else {
                # For ObjectTypes like "CustomShares" which don't have a corresponding cmdlet
                $total = 0
                Write-Log "No cmdlet associated with ObjectType $ObjectType. Proceeding with InputData only." -Level Info
            }
        }

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

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

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

        $count = 0
        $results = @()

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

                # Update progress using Show-ProgressHelper
                if ($total -gt 0) {
                    $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
                    }
                    "CustomShares" {
                        Write-Log "Error processing Share: $($_.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
    }
}