Providers/AD/Public/Get-ADEnvironmentReport.ps1

function Get-ADEnvironmentReport {
    <#
    .SYNOPSIS
        Generates a comprehensive report of the AD test environment

    .DESCRIPTION
        Creates detailed reports of all test data including users, devices, groups, service accounts, and OUs.
        All reports are always generated with complete details including group membership, full attribute sets,
        and comprehensive object information. Supports multiple output formats including
        console, JSON, HTML, and CSV.

    .PARAMETER OutputFormat
        Specifies the output format: Console, JSON, HTML, or CSV

    .PARAMETER OutputPath
        Path where the report file should be saved (for JSON, HTML, and CSV formats)

    .PARAMETER PassThru
        Returns the report data object instead of just displaying output

    .EXAMPLE
        Get-ADEnvironmentReport
        Displays a console report with complete details

    .EXAMPLE
        Get-ADEnvironmentReport -OutputFormat HTML -OutputPath "C:\Reports\ADTestReport.html"
        Creates a detailed HTML report with collapsible sections

    .EXAMPLE
        Get-ADEnvironmentReport -OutputFormat CSV -OutputPath "C:\Reports\"
        Creates separate CSV files for each object type with full details and group membership

    .EXAMPLE
        $reportData = Get-ADEnvironmentReport -PassThru
        Gets the complete report data object for further processing

    .OUTPUTS
        PSCustomObject with complete report data and statistics (when -PassThru is used)

    .NOTES
        Author: Jeffrey Stuhr
        Version: 1.1.0
        Last Updated: 2025-08-03

        All reports include:
        - Complete AD object attributes
        - Group membership details
        - Service account information
        - Full organizational structure
        - Comprehensive statistics
    #>


    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingWriteHost', '',
        Justification = 'Colour-coded console progress is intentional; results are returned as objects.')]
    [CmdletBinding()]
    [OutputType([PSCustomObject], ParameterSetName = 'PassThru')]
    [OutputType([void], ParameterSetName = 'Default')]
    param(
        [ValidateSet('Console', 'JSON', 'HTML', 'CSV')]
        [string]$OutputFormat = 'Console',

        [string]$OutputPath,

        [switch]$PassThru
    )

    begin {
        $correlationId = [System.Guid]::NewGuid()
        Write-Verbose "Starting Get-ADEnvironmentReport - CorrelationId: $correlationId"

        # Get domain information
        $domain = Get-ADTestDomain

        # Validate output path if specified
        if ($OutputPath -and $OutputFormat -ne 'Console') {
            if ($OutputFormat -eq 'CSV') {
                # For CSV, ensure it's a directory
                if (-not (Test-Path $OutputPath -PathType Container)) {
                    try {
                        New-Item -Path $OutputPath -ItemType Directory -Force | Out-Null
                    }
                    catch {
                        throw "Cannot create output directory: $OutputPath"
                    }
                }
            }
            else {
                # For single files, ensure directory exists
                $directory = Split-Path $OutputPath -Parent
                if ($directory -and -not (Test-Path $directory)) {
                    try {
                        New-Item -Path $directory -ItemType Directory -Force | Out-Null
                    }
                    catch {
                        throw "Cannot create output directory: $directory"
                    }
                }
            }
        }
    }

    process {
        try {
            Write-TestMessage -Message "Generating Active Directory Test Data Report" -Type Header

            # Initialize report data structure
            $reportData = @{
                GeneratedOn = Get-Date
                Domain = $domain.DNSName
                CorrelationId = $correlationId
                TestOUs = @()
                TestUsers = @()
                TestServiceAccounts = @()
                TestDevices = @()
                TestGroups = @()
                Summary = @{
                    TotalOUs = 0
                    TotalUsers = 0
                    TotalServiceAccounts = 0
                    TotalDevices = 0
                    TotalGroups = 0
                    TotalGroupMembers = 0
                }
            }

            # Collect Test OUs
            Write-TestMessage -Message "Collecting OU information..." -Type Info
            try {
                $searchBase = "OU=$($script:ADTestRootName),$($domain.DomainDN)"
                $ouProps = 'Description', 'ProtectedFromAccidentalDeletion'
                $testOUs = Get-ADOrganizationalUnit -Filter '*' -SearchBase $searchBase -Properties $ouProps |
                    Select-Object Name, DistinguishedName, Description, ProtectedFromAccidentalDeletion,
                        @{Name='ParentOU';Expression={($_.DistinguishedName -split ',',2)[1]}}

                $reportData.TestOUs = $testOUs
                $reportData.Summary.TotalOUs = $testOUs.Count
            }
            catch {
                Write-Warning "Error collecting OU data: $($_.Exception.Message)"
                $reportData.TestOUs = @()
            }

            # Collect Test Users
            Write-TestMessage -Message "Collecting user information..." -Type Info
            try {
                # Get ALL properties from AD
                $searchBase = "OU=Users,OU=$($script:ADTestRootName),$($domain.DomainDN)"
                $testUsers = Get-ADUser -Filter '*' -SearchBase $searchBase -Properties *

                $reportData.TestUsers = $testUsers
                $reportData.Summary.TotalUsers = $testUsers.Count
            }
            catch {
                Write-Warning "Error collecting user data: $($_.Exception.Message)"
                $reportData.TestUsers = @()
            }

            # Collect Test Service Accounts
            Write-TestMessage -Message "Collecting service account information..." -Type Info
            try {
                # Get ALL properties from AD
                $searchBase = "OU=ServiceAccounts,OU=$($script:ADTestRootName),$($domain.DomainDN)"
                $testServiceAccounts = Get-ADUser -Filter '*' -SearchBase $searchBase -Properties *

                $reportData.TestServiceAccounts = $testServiceAccounts
                $reportData.Summary.TotalServiceAccounts = $testServiceAccounts.Count
            }
            catch {
                Write-Warning "Error collecting service account data: $($_.Exception.Message)"
                $reportData.TestServiceAccounts = @()
            }

            # Collect Test Devices
            Write-TestMessage -Message "Collecting device information..." -Type Info
            try {
                # Get ALL properties from AD
                $searchBase = "OU=Devices,OU=$($script:ADTestRootName),$($domain.DomainDN)"
                $testDevices = Get-ADComputer -Filter '*' -SearchBase $searchBase -Properties *

                $reportData.TestDevices = $testDevices
                $reportData.Summary.TotalDevices = $testDevices.Count
            }
            catch {
                Write-Warning "Error collecting device data: $($_.Exception.Message)"
                $reportData.TestDevices = @()
            }

            # Collect Test Groups
            Write-TestMessage -Message "Collecting group information..." -Type Info
            try {
                # Get ALL properties from AD
                $searchBase = "OU=Groups,OU=$($script:ADTestRootName),$($domain.DomainDN)"
                $testGroups = Get-ADGroup -Filter '*' -SearchBase $searchBase -Properties *

                # Always collect group members and add member counts
                $groupMembers = @()
                foreach ($group in $testGroups) {
                    try {
                        $getADGroupMemberArgs1 = @{
                            Identity    = $group.DistinguishedName
                            ErrorAction = 'SilentlyContinue'
                        }
                        $members = Get-ADGroupMember @getADGroupMemberArgs1
                        $countProp = @{
                            MemberType = 'NoteProperty'
                            Name       = 'MemberCount'
                            Value      = $members.Count
                            Force      = $true
                        }
                        $group | Add-Member @countProp

                        $memberSummary = $members | Select-Object Name, objectClass
                        $memberProp = @{
                            MemberType = 'NoteProperty'
                            Name       = 'Members'
                            Value      = $memberSummary
                            Force      = $true
                        }
                        $group | Add-Member @memberProp

                        # Create individual member records for the GroupMembers collection
                        foreach ($member in $members) {
                            $groupMembers += [PSCustomObject]@{
                                GroupName = $group.Name
                                GroupDistinguishedName = $group.DistinguishedName
                                MemberName = $member.Name
                                MemberType = $member.objectClass
                                MemberDistinguishedName = $member.DistinguishedName
                                    MemberSamAccountName = $member.SamAccountName
                                }
                            }
                        }
                        catch {
                            $group | Add-Member -MemberType NoteProperty -Name 'MemberCount' -Value 0 -Force
                            $group | Add-Member -MemberType NoteProperty -Name 'Members' -Value @() -Force
                        }
                    }
                    $reportData.GroupMembers = $groupMembers
                    $reportData.Summary.TotalGroupMembers = $groupMembers.Count

                $reportData.TestGroups = $testGroups
                $reportData.Summary.TotalGroups = $testGroups.Count
            }
            catch {
                Write-Warning "Error collecting group data: $($_.Exception.Message)"
                $reportData.TestGroups = @()
            }

            # Generate output based on format using dedicated helper functions
            switch ($OutputFormat) {
                'Console' {

                    # Always show detailed information
                    if ($reportData.TestOUs.Count -gt 0) {
                        Write-Host "=== ORGANIZATIONAL UNITS ===" -ForegroundColor Yellow
                        $reportData.TestOUs |
                        Format-Table Name, Description, ProtectedFromAccidentalDeletion -AutoSize
                    }

                    if ($reportData.TestUsers.Count -gt 0) {
                        Write-Host "=== USERS ===" -ForegroundColor Yellow
                        $reportData.TestUsers | Format-Table Name, Department, Title, Enabled -AutoSize
                    }

                    if ($reportData.TestServiceAccounts.Count -gt 0) {
                        Write-Host "=== SERVICE ACCOUNTS ===" -ForegroundColor Yellow
                        $reportData.TestServiceAccounts |
                        Format-Table Name, SamAccountName, Description, Enabled -AutoSize
                    }

                    if ($reportData.TestDevices.Count -gt 0) {
                        Write-Host "=== DEVICES ===" -ForegroundColor Yellow
                        $reportData.TestDevices | Format-Table Name, OperatingSystem, Enabled -AutoSize
                    }

                    if ($reportData.TestGroups.Count -gt 0) {
                        Write-Host "=== SECURITY GROUPS ===" -ForegroundColor Yellow
                        $reportData.TestGroups | Format-Table Name, GroupScope, MemberCount -AutoSize
                    }
                    Write-TestMessage -Message "Active Directory Test Data Report" -Type Success
                    Write-Host ""
                    Write-Host "Domain: $($reportData.Domain)" -ForegroundColor Cyan
                    Write-Host "Generated: $($reportData.GeneratedOn)" -ForegroundColor Cyan
                    Write-Host ""

                    Write-Host "=== SUMMARY ===" -ForegroundColor Yellow
                    Write-Host "Total OUs: $($reportData.Summary.TotalOUs)" -ForegroundColor Green
                    Write-Host "Total Users: $($reportData.Summary.TotalUsers)" -ForegroundColor Green
                    Write-Host ("Total Service Accounts: " +
                        "$($reportData.Summary.TotalServiceAccounts)") -ForegroundColor Green
                    Write-Host "Total Devices: $($reportData.Summary.TotalDevices)" -ForegroundColor Green
                    Write-Host "Total Groups: $($reportData.Summary.TotalGroups)" -ForegroundColor Green
                    Write-Host ("Total Group Members: " +
                        "$($reportData.Summary.TotalGroupMembers)") -ForegroundColor Green
                    Write-Host ""
                }

                'JSON' {
                    if (-not $OutputPath) {
                        $OutputPath = "ADTestReport_$(Get-Date -Format 'yyyyMMdd_HHmmss').json"
                    }

                    New-ADTestJSONReport -ReportData $reportData -OutputPath $OutputPath
                }

                'HTML' {
                    if (-not $OutputPath) {
                        $OutputPath = "ADTestReport_$(Get-Date -Format 'yyyyMMdd_HHmmss').html"
                    }

                    New-ADTestHTMLReport -ReportData $reportData -OutputPath $OutputPath
                }

                'CSV' {
                    if (-not $OutputPath) {
                        $OutputPath = ".\ADTestReport_$(Get-Date -Format 'yyyyMMdd_HHmmss')"
                    }

                    New-ADTestCSVReport -ReportData $reportData -OutputPath $OutputPath
                }
            }

            # Return data only if PassThru is specified
            if ($PassThru) {
                # Convert hashtable to PSCustomObject for better usability
                return [PSCustomObject]$reportData
            }

        } catch {
            Write-Error "Failed to generate report: $($_.Exception.Message)" -ErrorAction Stop
        }
    }

    end {
        Write-Verbose "Completed Get-ADEnvironmentReport - CorrelationId: $correlationId"
    }
}