Public/Get-ForestInventory.ps1

function Get-ForestInventory {
    [CmdletBinding()]
    param(
        [string]$ObjectType = "ForestInfo",
        [switch]$Export,
        [string]$ExportPath = $script:Config.ExportPath
    )
    
    try {
        Write-Log "Retrieving comprehensive forest information..." -Level Info

        # Retrieve forest info using a similar approach as previously demonstrated
        $forest = Invoke-WithRetry -ScriptBlock {
            Get-ADForest -ErrorAction Stop
        }

        # For Forest Info Collection:
        $forestInfo = [PSCustomObject]@{
            ForestRootDomain   = $forest.RootDomain
            ForestMode         = $forest.ForestMode
            GlobalCatalogs     = $forest.GlobalCatalogs
            Domains            = $forest.Domains
            SchemaMaster       = $forest.SchemaMaster
            DomainNamingMaster = $forest.DomainNamingMaster
        }

        # For Domain Info Collection:
        $domainInfoObjects = foreach ($domainName in $forest.Domains) {
            $domain = Invoke-WithRetry -ScriptBlock {
                Get-ADDomain -Identity $domainName -ErrorAction Stop
            }

            # Get domain controllers with PSCustomObject
            $domainControllers = Get-ADDomainController -Filter "Domain -eq '$domainName'" -ErrorAction SilentlyContinue | 
            ForEach-Object {
                [PSCustomObject]@{
                    HostName               = $_.HostName
                    IPv4Address            = $_.IPv4Address
                    Site                   = $_.Site
                    IsGlobalCatalog        = $_.IsGlobalCatalog
                    OperatingSystem        = $_.OperatingSystem
                    OperatingSystemVersion = $_.OperatingSystemVersion
                    Enabled                = $_.Enabled
                }
            }

            [PSCustomObject]@{
                DomainName           = $domainName
                DomainMode           = $domain.DomainMode
                PDCEmulator          = $domain.PDCEmulator
                RIDMaster            = $domain.RIDMaster
                InfrastructureMaster = $domain.InfrastructureMaster
                DomainControllers    = $domainControllers
            }
        }

        # For Trust Collection:
        $trustObjects = Get-ADTrust -Filter * -Server $forest.RootDomain -ErrorAction SilentlyContinue | 
        ForEach-Object {
            [PSCustomObject]@{
                Name      = $_.Name
                Source    = $_.Source
                Target    = $_.Target
                TrustType = $_.TrustType
                Direction = $_.Direction
                TGTQuota  = $_.TGTQuota
                Status    = try {
                    Test-ADTrust -Identity $_.Name -ErrorAction Stop
                    "Valid"
                }
                catch {
                    "Invalid: $($_.Exception.Message)"
                }
            }
        }

        # For Sites Collection:
        $siteObjects = Get-ADReplicationSite -Filter * -ErrorAction SilentlyContinue | 
        ForEach-Object {
            $site = $_
            $subnets = Get-ADReplicationSubnet -Filter * -ErrorAction SilentlyContinue | 
            Where-Object { $_.Site -eq $site.DistinguishedName } |
            ForEach-Object {
                [PSCustomObject]@{
                    Name        = $_.Name
                    Site        = $_.Site
                    Location    = $_.Location
                    Description = $_.Description
                }
            }

            # Get site links
            $siteLinks = Get-ADReplicationSiteLink -Filter * -ErrorAction SilentlyContinue |
            Where-Object { $_.Sites -contains $site.DistinguishedName } |
            ForEach-Object {
                [PSCustomObject]@{
                    Name                          = $_.Name
                    Cost                          = $_.Cost
                    ReplicationFrequencyInMinutes = $_.ReplicationFrequencyInMinutes
                    Sites                         = $_.Sites
                }
            }

            [PSCustomObject]@{
                SiteName    = $site.Name
                Description = $site.Description
                Location    = $site.Location
                Subnets     = $subnets
                SiteLinks   = $siteLinks
                Created     = $site.Created
                Modified    = $site.Modified
            }
        }

        # Then add these to $forestInfo:
        $forestInfo | Add-Member -MemberType NoteProperty -Name DomainInfo -Value $domainInfoObjects
        $forestInfo | Add-Member -MemberType NoteProperty -Name Trusts -Value $trustObjects
        $forestInfo | Add-Member -MemberType NoteProperty -Name Sites -Value $siteObjects

        # Export data
        Export-ADData -ObjectType $ObjectType -Data $forestInfo -ExportPath $ExportPath 

        return $forestInfo
    }
    catch {
        Write-Log "Failed to retrieve forest information: $($_.Exception.Message)" -Level Error
        Show-ErrorBox "Insufficient permissions or unable to retrieve forest info."
    }
}