Private/Operations/Invoke-RemovalWorkflow.ps1

#Requires -Version 5.1

<#
.SYNOPSIS
    Orchestration workflow module for SID removal operations
 
.DESCRIPTION
    Provides comprehensive workflow orchestration for SID removal operations,
    coordinating security validation, ACL manipulation, backup creation, and
    verification. This module implements the "controller" pattern for complex
    multi-step operations while maintaining clean separation of concerns.
 
    This module focuses solely on workflow coordination, delegating specific
    tasks to specialized modules for maximum modularity and testability.
 
.NOTES
    Author: Jeffrey Stuhr
    Blog: https://www.techbyjeff.net
    LinkedIn: https://www.linkedin.com/in/jeffrey-stuhr-034214aa/
    Last Updated: 2025-01-27
    Version: 1.0.0
    PowerShell Version: 5.1+ (Windows), 7.x+ (Cross-platform)
 
    TROUBLESHOOTING:
    - For workflow failures: .\Troubleshooting\Common\Workflow-Issues.md
    - For integration issues: .\Troubleshooting\Common\Module-Integration-Issues.md
 
    DEPENDENCIES:
    - Requires Security\Invoke-SecurityValidation.ps1 for security validation
    - Requires ACL\Get-ACLForRemoval.ps1 for ACL retrieval
    - Requires ACL\Invoke-SIDRemoval.ps1 for ACL manipulation
    - Requires ACL\Set-ModifiedACL.ps1 for ACL application
    - Requires Verification\Invoke-RemovalVerification.ps1 for verification
    - Requires RemovalLogging\Write-RemovalSecurityLog.ps1 for security logging
    - Requires New-ACLBackup.ps1 for backup operations
    - Requires Classes.ps1 for RemovalOperationResult type
    - Requires Logging.ps1 for Write-StructuredLog function
#>


function Invoke-RemovalWorkflow {
    <#
    .SYNOPSIS
        Orchestrates comprehensive SID removal workflow operations
 
    .DESCRIPTION
        Coordinates the complete SID removal workflow including security validation,
        backup creation, ACL manipulation, application, and verification. This function
        implements the "controller" pattern, delegating specific tasks to specialized
        modules while maintaining overall workflow integrity.
 
        The workflow includes:
        - Parameter validation and preprocessing
        - Security validation and risk assessment
        - Optional ACL backup creation
        - ACL manipulation and SID removal
        - ACL application to Active Directory
        - Post-operation verification
        - Comprehensive audit logging and tracking
 
    .PARAMETER ObjectDN
        Distinguished name of the Active Directory object to process.
        Object must exist and be accessible with current credentials.
 
    .PARAMETER OrphanedSIDs
        Array of orphaned Security Identifiers to remove from the object.
        SIDs will be validated against security policies before removal.
 
    .PARAMETER BackupPath
        Optional directory path for storing ACL backups before removal operations.
        Backups include metadata and integrity verification.
 
    .PARAMETER WhatIfMode
        When specified, performs validation and shows what would be removed
        without making actual changes. Useful for preview and approval workflows.
 
    .PARAMETER CorrelationId
        Unique identifier for tracking this removal operation across logs
        and audit trails. Generated automatically if not provided.
 
    .EXAMPLE
        PS> $result = Invoke-RemovalWorkflow -ObjectDN "CN=TestUser,CN=Users,DC=contoso,DC=com" -OrphanedSIDs @("S-1-5-21-1234567890-1001")
 
        DESCRIPTION: Orchestrates removal workflow for a single SID
        OUTPUT: RemovalOperationResult with comprehensive operation details
        USE CASE: Targeted cleanup with full workflow validation
 
    .EXAMPLE
        PS> $result = Invoke-RemovalWorkflow -ObjectDN $dn -OrphanedSIDs $sids -BackupPath "C:\Backups" -WhatIfMode -CorrelationId $id
 
        DESCRIPTION: Preview workflow with backup preparation and correlation tracking
        OUTPUT: RemovalOperationResult showing planned operations without changes
        USE CASE: Pre-approval validation for enterprise change management
 
    .OUTPUTS
        [RemovalOperationResult] Object containing comprehensive operation results:
        - ObjectDN: Distinguished name of processed object
        - CorrelationId: Unique tracking identifier
        - IntendedRemovals: Number of SIDs planned for removal
        - ActualRemovals: Number of SIDs successfully removed
        - FailedRemovals: Number of SIDs that failed removal
        - RemovedSIDs: Array of successfully removed SIDs
        - FailedSIDs: Array of SIDs that failed removal
        - BlockedSIDs: Array of SIDs blocked by security policies
        - Success: Overall operation success status
        - ErrorMessage: Detailed error information if applicable
        - ProcessingTime: Duration of the complete workflow
        - SecurityValidation: Comprehensive security validation results
 
    .NOTES
        WORKFLOW FEATURES:
        - Modular design with specialized component delegation
        - Comprehensive error handling with rollback capability
        - Security-first approach with validation gates
        - Enterprise audit trail with correlation tracking
        - Flexible backup and verification options
 
        PERFORMANCE CHARACTERISTICS:
        - Processing Time: 500ms-2s per object depending on complexity
        - Memory Usage: Optimized with efficient object handling
        - Reliability: Multi-layer validation and verification
        - Scalability: Designed for enterprise-scale operations
 
        SECURITY CONTROLS:
        - Multi-layer security validation before any changes
        - Protected SID detection and blocking
        - Risk-based operation controls
        - Comprehensive audit logging throughout workflow
        - Backup creation for rollback capability
 
        TROUBLESHOOTING:
        - For security blocks: Review security validation results
        - For ACL failures: Check AD permissions and connectivity
        - For verification issues: Consider AD replication timing
        - For performance: Review object complexity and network latency
    #>


    [CmdletBinding(SupportsShouldProcess)]
    [OutputType([RemovalOperationResult])]
    param(
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$ObjectDistinguishedName,

        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string[]]$OrphanedSIDs,

        [Parameter()]
        [string]$BackupPath,

        [Parameter()]
        [switch]$WhatIfMode,

        [Parameter()]
        [string]$CorrelationId = [System.Guid]::NewGuid().ToString()
    )

    # Initialize result object
    $result = [RemovalOperationResult]::new()
    $result.ObjectDN = $ObjectDistinguishedName
    $result.CorrelationId = $CorrelationId
    $result.IntendedRemovals = $OrphanedSIDs.Count

    $stopwatch = [System.Diagnostics.Stopwatch]::StartNew()

    try {
        Write-StructuredLog "Starting SID removal workflow for $ObjectDistinguishedName (SIDs: $($OrphanedSIDs.Count), WhatIf: $WhatIfMode)" -Level Verbose -CorrelationId $CorrelationId

        # Phase 1: Parameter Validation
        Write-StructuredLog "Phase 1: Parameter validation" -Level Debug -CorrelationId $CorrelationId
        if ([string]::IsNullOrWhiteSpace($ObjectDistinguishedName.Trim())) {
            throw "ObjectDN parameter cannot be empty or whitespace"
        }

        # Phase 2: ACL Retrieval
        Write-StructuredLog "Phase 2: ACL retrieval" -Level Debug -CorrelationId $CorrelationId
        $acl = Get-ACLForRemoval -ObjectDistinguishedName $ObjectDistinguishedName -CorrelationId $CorrelationId

        # Phase 3: Security Validation
        Write-StructuredLog "Phase 3: Security validation" -Level Debug -CorrelationId $CorrelationId
        $securityValidation = Invoke-SecurityValidation -OrphanedSIDs $OrphanedSIDs -ObjectDN $ObjectDistinguishedName -CurrentDomainSID $script:CurrentDomainSID -CorrelationId $CorrelationId

        $result.SecurityValidation = $securityValidation
        $result.BlockedSIDs = $securityValidation.BlockedSIDs

        if (-not $securityValidation.IsValid) {
            $result.ErrorMessage = "Security validation failed: $($securityValidation.Issues -join '; ')"
            $result.Success = $false
            Write-StructuredLog "SECURITY BLOCK: $($result.ErrorMessage)" -Level Error -CorrelationId $CorrelationId
            return $result
        }

        # Phase 4: Backup Creation (if requested and not WhatIf mode)
        if (-not $WhatIfMode -and $BackupPath) {
            Write-StructuredLog "Phase 4: Backup creation" -Level Debug -CorrelationId $CorrelationId
            $backupSuccess = New-ACLBackup -ObjectDN $ObjectDistinguishedName -ACL $acl -BackupPath $BackupPath -CorrelationId $CorrelationId
            if (-not $backupSuccess) {
                throw "Failed to create ACL backup - aborting removal operation"
            }
        }

        # Phase 5: SID Removal Processing
        Write-StructuredLog "Phase 5: SID removal processing" -Level Debug -CorrelationId $CorrelationId
        $removalResults = Invoke-SIDRemoval -ACL $acl -AllowedSIDs $securityValidation.AllowedSIDs -ObjectDN $ObjectDistinguishedName -WhatIfMode:$WhatIfMode -CorrelationId $CorrelationId

        $result.RemovedSIDs = $removalResults.RemovedSIDs
        $result.FailedSIDs = $removalResults.FailedSIDs
        $result.ActualRemovals = $removalResults.RemovedSIDs.Count
        $result.FailedRemovals = $removalResults.FailedSIDs.Count

        # Phase 6: Security Audit Logging
        Write-RemovalSecurityLog -SecurityEventType 'PrivilegeUse' -Message "ACL modification operation initiated" -Outcome 'Attempt' -CorrelationId $CorrelationId -SecurityContext @{
            Operation = 'RemoveOrphanedSIDs'
            TargetObjectDN = $ObjectDistinguishedName
            SIDsToRemove = $removalResults.RemovedSIDs.Count
            SIDsFailedRemoval = $removalResults.FailedSIDs.Count
            WhatIfMode = $WhatIfMode
            BackupCreated = (-not $WhatIfMode -and $BackupPath)
            SecurityValidation = @{
                IsValid = $securityValidation.IsValid
                RiskLevel = $securityValidation.RiskLevel
                BlockedSIDs = $securityValidation.BlockedSIDs.Count
            }
        }

        # Phase 7: ACL Application (if not WhatIf mode and changes exist)
        if (-not $WhatIfMode -and $removalResults.RemovedSIDs.Count -gt 0) {
            Write-StructuredLog "Phase 7: ACL application" -Level Debug -CorrelationId $CorrelationId

            if ($PSCmdlet.ShouldProcess($ObjectDistinguishedName, "Apply SID removal changes")) {
                $applicationSuccess = Set-ModifiedACL -ACL $removalResults.ModifiedACL -ObjectDN $ObjectDistinguishedName -CorrelationId $CorrelationId

                # Set-ModifiedACL returns an ACLApplicationResult, which is truthy even when the write
                # failed; only its Success property says whether the ACL was applied
                if ($applicationSuccess.Success) {
                    # Log successful operation
                    Write-RemovalSecurityLog -SecurityEventType 'PrivilegeUse' -Message "ACL modification operation completed successfully" -Outcome 'Success' -CorrelationId $CorrelationId -SecurityContext @{
                        Operation = 'RemoveOrphanedSIDs'
                        TargetObjectDN = $ObjectDistinguishedName
                        SIDsRemoved = $removalResults.RemovedSIDs
                        ActualRemovals = $result.ActualRemovals
                        BackupPath = $BackupPath
                        SecurityValidationPassed = $true
                    }

                    # Phase 8: Verification
                    Write-StructuredLog "Phase 8: Operation verification" -Level Debug -CorrelationId $CorrelationId
                    $verificationResult = Invoke-RemovalVerification -ObjectDN $ObjectDistinguishedName -AllowedSIDs $securityValidation.AllowedSIDs -CorrelationId $CorrelationId
                    $result.Success = $verificationResult.Success

                    if (-not $verificationResult.Success) {
                        $result.ErrorMessage = $verificationResult.ErrorMessage
                        Write-RemovalSecurityLog -SecurityEventType 'PrivilegeUse' -Message "ACL modification verification failed" -Outcome 'Failure' -CorrelationId $CorrelationId -SecurityContext @{
                            Operation = 'RemoveOrphanedSIDs'
                            TargetObjectDN = $ObjectDistinguishedName
                            VerificationError = $verificationResult.ErrorMessage
                            RemainingOrphanedSIDs = $verificationResult.RemainingOrphanedSIDs
                        }
                    }
                } else {
                    $result.Success = $false
                    $result.ErrorMessage = "Failed to apply ACL changes"
                    Write-RemovalSecurityLog -SecurityEventType 'PrivilegeUse' -Message "ACL modification application failed" -Outcome 'Failure' -CorrelationId $CorrelationId -SecurityContext @{
                        Operation = 'RemoveOrphanedSIDs'
                        TargetObjectDN = $ObjectDistinguishedName
                        Error = 'ACLApplicationFailure'
                        IntendedRemovals = $result.IntendedRemovals
                    }
                }
            }
        } elseif ($WhatIfMode) {
            $result.Success = $true
            Write-StructuredLog "WhatIf mode: No changes applied" -Level Verbose -CorrelationId $CorrelationId
        } else {
            $result.Success = $true  # No changes needed
            Write-StructuredLog "No SIDs required removal" -Level Verbose -CorrelationId $CorrelationId
        }

        Write-StructuredLog "SID removal workflow completed for $ObjectDistinguishedName - Success: $($result.Success), Removed: $($result.ActualRemovals), Failed: $($result.FailedRemovals)" -Level Verbose -CorrelationId $CorrelationId
        return $result
    }
    catch {
        $result.Success = $false
        $result.ErrorMessage = "Workflow failed: $($_.Exception.Message)"
        Write-StructuredLog "SID removal workflow failed for $ObjectDistinguishedName : $($_.Exception.Message)" -Level Error -CorrelationId $CorrelationId

        # Log workflow failure for security audit
        Write-RemovalSecurityLog -SecurityEventType 'PrivilegeUse' -Message "SID removal workflow failed" -Outcome 'Failure' -CorrelationId $CorrelationId -SecurityContext @{
            Operation = 'RemoveOrphanedSIDs'
            TargetObjectDN = $ObjectDistinguishedName
            Error = $_.Exception.Message
            IntendedRemovals = $result.IntendedRemovals
        }

        return $result
    }
    finally {
        $stopwatch.Stop()
        $result.ProcessingTime = $stopwatch.Elapsed
        Write-StructuredLog "Workflow processing time: $($result.ProcessingTime.TotalMilliseconds)ms" -Level Debug -CorrelationId $CorrelationId
    }
}