Providers/AD/Public/New-ADTestOUStructure.ps1

function New-ADTestOUStructure {
    <#
    .EXTERNALHELP TestEnvironment-Help.xml
    .SYNOPSIS
        Creates the standardized organizational unit (OU) structure for AD test data.
    #>


    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingWriteHost', '',
        Justification = 'Colour-coded console progress is intentional; results are returned as objects.')]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '',
        Justification = 'Every creation goes through New-ADTestOU, which calls ShouldProcess.')]
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
    [OutputType([PSCustomObject])]
    param(
        [switch]$PassThru
    )

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

        # Test prerequisites
        if (-not (Test-ADTestPrerequisite)) {
            throw ("Prerequisites not met. Please ensure Active Directory module is installed and you have " +
                "appropriate permissions.")
        }
    }

    process {
        try {
            Write-Host ""
            Write-Host "=========================================" -ForegroundColor Cyan
            Write-Host " Active Directory TestData OU Structure Creation" -ForegroundColor Cyan
            Write-Host "=========================================" -ForegroundColor Cyan

            # Initialize results
            $results = @{
                Created = @()
                Skipped = @()
                Failed = @()
            }

            # Get domain information
            $domain = Get-ADTestDomain
            Write-Verbose "Detected domain: $($domain.DomainName) (DN: $($domain.DomainDN))"

            # Create main TestData OU
            Write-TestMessage -Message "Creating main TestData OU..." -Type Info
            $result = New-ADTestOU -Name $script:ADTestRootName -Path $domain.DomainDN -Description ("Root OU for all test " +
                "data objects")
            switch ($result.Action) {
                'Created' { $results.Created += $script:ADTestRootName }
                'Skipped' { $results.Skipped += $script:ADTestRootName }
                'Failed' { $results.Failed += ("{0}: {1}" -f $script:ADTestRootName, $result.Message) }
            }

            $testDataOU = "OU=$($script:ADTestRootName),$($domain.DomainDN)"

            # Create Users OU
            $result = New-ADTestOU -Name "Users" -Path $testDataOU -Description ("Test user accounts organized " +
                "by department")
            switch ($result.Action) {
                'Created' { $results.Created += "Users" }
                'Skipped' { $results.Skipped += "Users" }
                'Failed' { $results.Failed += "Users: $($result.Message)" }
            }

            $usersOU = "OU=Users,$testDataOU"

            # Create Group category sub-OUs
            Write-TestMessage -Message "Creating Groups category sub-OUs..." -Type Info

            # Create Groups OU first
            $groupsOU = "OU=Groups,$testDataOU"
            $result = New-ADTestOU -Name "Groups" -Path $testDataOU -Description ("Security groups organized by " +
                "category")
            switch ($result.Action) {
                'Created' { $results.Created += "Groups" }
                'Skipped' { $results.Skipped += "Groups" }
                'Failed' { $results.Failed += "Groups: $($result.Message)" }
            }

            $groupCategories = @("Department", "Role", "Location", "Device", "Resource", "Administrative")
            foreach ($category in $groupCategories) {
                $result = New-ADTestOU -Name $category -Path $groupsOU -Description ("$category-based security " +
                    "groups")
                switch ($result.Action) {
                    'Created' { $results.Created += "Groups\$category" }
                    'Skipped' { $results.Skipped += "Groups\$category" }
                    'Failed' { $results.Failed += "Groups\$category`: $($result.Message)" }
                }
            }

            # Create Device type sub-OUs
            Write-TestMessage -Message "Creating Device type sub-OUs..." -Type Info

            # Create Devices OU first
            $devicesOU = "OU=Devices,$testDataOU"
            $result = New-ADTestOU -Name "Devices" -Path $testDataOU -Description ("Computer and device objects " +
                "organized by type")
            switch ($result.Action) {
                'Created' { $results.Created += "Devices" }
                'Skipped' { $results.Skipped += "Devices" }
                'Failed' { $results.Failed += "Devices: $($result.Message)" }
            }

            $deviceTypes = @("Workstations", "Servers", "Printers", "Mobile")
            foreach ($deviceType in $deviceTypes) {
                $result = New-ADTestOU -Name $deviceType -Path $devicesOU -Description "$deviceType devices"
                switch ($result.Action) {
                    'Created' { $results.Created += "Devices\$deviceType" }
                    'Skipped' { $results.Skipped += "Devices\$deviceType" }
                    'Failed' { $results.Failed += "Devices\$deviceType`: $($result.Message)" }
                }
            }

            # Create ServiceAccounts OU
            $result = New-ADTestOU -Name "ServiceAccounts" -Path $testDataOU -Description ("Service account " +
                "objects for applications and services")
            switch ($result.Action) {
                'Created' { $results.Created += "ServiceAccounts" }
                'Skipped' { $results.Skipped += "ServiceAccounts" }
                'Failed' { $results.Failed += "ServiceAccounts: $($result.Message)" }
            }

            # Create department-based user OUs
            Write-TestMessage -Message "Analyzing departments from ADUsers.csv..." -Type Info
            $dataPath = Get-ADTestDataPath
            $csvPath = Join-Path $dataPath "ADUsers.csv"
            $departments = @()

            if (Test-Path $csvPath) {
                $departments = (Import-csv $csvPath |
                        Select-Object -ExpandProperty Department |
                        Sort-Object -Unique) |
                    Where-Object { ![string]::IsNullOrWhiteSpace($_) }
                Write-TestMessage -Message "Creating department sub-OUs..." -Type Info

                foreach ($dept in $departments) {
                    # Clean department name for OU creation
                    $cleanDept = $dept -replace '[^a-zA-Z0-9\s]', '' -replace '\s+', ' '
                    $cleanDept = $cleanDept.Trim()

                    if (![string]::IsNullOrWhiteSpace($cleanDept)) {
                        $result = New-ADTestOU -Name $cleanDept -Path $usersOU -Description ("Users in the " +
                            "$dept department")
                        switch ($result.Action) {
                            'Created' { $results.Created += "Users\$cleanDept" }
                            'Skipped' { $results.Skipped += "Users\$cleanDept" }
                            'Failed' { $results.Failed += "Users\$cleanDept`: $($result.Message)" }
                        }
                    }
                }
            } else {
                Write-TestMessage -Message ("ADUsers.csv not found - department OUs will be created during " +
                    "user creation") -Type Warning
            }

            # Display summary
            Write-Verbose "TestData OU Structure:"
            Write-Verbose " TestData"
            Write-Verbose " Users"

            foreach ($dept in $departments) {
                $cleanDept = $dept -replace '[^a-zA-Z0-9\s]', '' -replace '\s+', ' '
                $cleanDept = $cleanDept.Trim()
                if (![string]::IsNullOrWhiteSpace($cleanDept)) {
                    Write-Verbose " $cleanDept"
                }
            }

            Write-Verbose " Groups"
            foreach ($category in $groupCategories) {
                Write-Verbose " $category"
            }

            Write-Verbose " Devices"
            foreach ($deviceType in $deviceTypes) {
                Write-Verbose " $deviceType"
            }

            Write-Verbose " ServiceAccounts"

            # Display OU Creation Summary
            Write-TestMessage -Message "OU Creation Summary" -Type Info
            Write-TestMessage -Message " OUs Created: $($results.Created.Count)" -Type Info
            Write-TestMessage -Message " OUs Skipped: $($results.Skipped.Count)" -Type Warning
            if ($results.Failed.Count -gt 0) {
                Write-TestMessage -Message " OUs Failed: $($results.Failed.Count)" -Type Error
            }

            if ($WhatIfPreference) {
                Write-TestMessage -Message "WHATIF: Organizational structure analysis complete!" -Type Warning
                Write-TestMessage -Message "Run without -WhatIf to create the actual OUs." -Type Warning
            } else {
                Write-TestMessage -Message "Organizational structure creation complete!" -Type Success
            }

            if ($PassThru) {
                return [PSCustomObject]$results
            }

        } catch {
            Write-Error "Failed to create organizational structure: $($_.Exception.Message)" -ErrorAction Stop
        }
    }

    end {
        Write-Verbose "Completed New-ADTestOUStructure - CorrelationId: $correlationId"
    }
}