Scripts/Run-MaturityAssessment.ps1

# Maturity Model Assessment Framework - Main Script
# This script orchestrates the complete assessment workflow:
# 1. Collect configuration data from Microsoft Purview
# 2. Run control book assessments based on specified configuration
# 3. Generate reports in CSV and Excel formats

param(
    [Parameter(Mandatory = $false)]
    [string]$ConfigurationName = "PSPF",
    
    [Parameter(Mandatory = $false)]
    [switch]$SkipDataCollection = $false,
    
    [Parameter(Mandatory = $false)]
    [switch]$GenerateExcel
)

# Import required functions
. "$PSScriptRoot\..\Private\Test-ControlBook.ps1"

# Define paths based on configuration
$configBasePath = "$PSScriptRoot\..\config"
$outputPath = "$PSScriptRoot\..\output"
$dataCollectionScript = "$PSScriptRoot\..\Collect-PurviewConfiguration.ps1"

# Create output directory if it doesn't exist
if (-not (Test-Path $outputPath)) {
    New-Item -Path $outputPath -ItemType Directory -Force | Out-Null
}

# Configuration-specific file paths
$controlConfigPath = "$configBasePath\ControlBook_${ConfigurationName}_Config.csv"
$propertyConfigPath = "$configBasePath\ControlBook_Property_${ConfigurationName}_Config.csv"
$runLogPath = "$outputPath\file_runlog.txt"

# Dynamic file names will be set after getting the OptimizedReport path
$resultsPath = $null
$excelReportPath = $null

# Function to get the latest OptimizedReport JSON file from run log
function Get-LatestOptimizedReport {
    param([string]$RunLogPath, [string]$OutputPath)
    
    if (Test-Path $RunLogPath) {
        $logEntries = Get-Content $RunLogPath | Where-Object { $_ -match "OptimizedReport.*\.json" }
        if ($logEntries) {
            $latestEntry = $logEntries[-1] # Get the last entry
            # Extract filename from log entry: "2025-07-09 12:47:20 - OptimizedReport: OptimizedReport_xxx.json"
            if ($latestEntry -match "OptimizedReport:\s+(OptimizedReport_.*\.json)") {
                $fileName = $matches[1]
                $fullPath = Join-Path $OutputPath $fileName
                if (Test-Path $fullPath) {
                    return $fullPath
                }
            }
        }
    }
    
    # Fallback: search for OptimizedReport*.json files directly
    if (Test-Path $OutputPath) {
        $jsonFiles = Get-ChildItem -Path $OutputPath -Filter "OptimizedReport*.json" -ErrorAction SilentlyContinue | Sort-Object LastWriteTime -Descending
        if ($jsonFiles) {
            return $jsonFiles[0].FullName
        }
    }
    
    return $null
}

# Function to extract tenant ID from OptimizedReport filename and create dynamic result file names
function Set-DynamicResultPaths {
    param(
        [string]$OptimizedReportPath,
        [string]$ConfigurationName,
        [string]$OutputPath
    )
    
    # Extract tenant ID from OptimizedReport filename
    # Format: OptimizedReport_${TenantId}_$(timestamp).json
    $fileName = Split-Path -Leaf $OptimizedReportPath
    if ($fileName -match "OptimizedReport_([a-f0-9]+)_(\d{14})\.json") {
        $tenantId = $matches[1]
        $timestamp = Get-Date -Format 'yyyyMMddHHmmss'
        
        # Create dynamic file names following the same pattern
        $resultsFileName = "TestResults_${ConfigurationName}_${tenantId}_${timestamp}.csv"
            $excelFileName = "TestResults_${ConfigurationName}_${tenantId}_${timestamp}.xlsx"
        
        return @{
            ResultsPath = Join-Path $OutputPath $resultsFileName
            ExcelPath = Join-Path $OutputPath $excelFileName
            TenantId = $tenantId
        }
    } else {
        # Fallback to original naming if pattern doesn't match
        $timestamp = Get-Date -Format 'yyyyMMddHHmmss'
            $resultsFileName = "TestResults_${ConfigurationName}_${timestamp}.csv"
            $excelFileName = "TestResults_${ConfigurationName}_${timestamp}.xlsx"
            return @{
                ResultsPath = Join-Path $OutputPath $resultsFileName
                ExcelPath = Join-Path $OutputPath $excelFileName
                TenantId = "unknown"
            }
    }
}

Write-Host "=== Maturity Model Assessment Framework ===" -ForegroundColor Cyan
Write-Host "Configuration: $ConfigurationName" -ForegroundColor White
Write-Host "Start Time: $(Get-Date)" -ForegroundColor White
Write-Host ""

# Step 1: Data Collection (if not skipped)
if (-not $SkipDataCollection) {
    Write-Host "Step 1: Collecting Microsoft Purview Configuration Data..." -ForegroundColor Yellow
    Write-Host "Running data collection script: $dataCollectionScript"
    
    try {
        & $dataCollectionScript
        
        # Get the latest OptimizedReport JSON file
        $optimizedReportPath = Get-LatestOptimizedReport -RunLogPath $runLogPath -OutputPath $outputPath
        
        if ($optimizedReportPath -and (Test-Path $optimizedReportPath)) {
            Write-Host "✅ Data collection completed successfully" -ForegroundColor Green
            $reportSize = (Get-Item $optimizedReportPath).Length / 1MB
            Write-Host " Using OptimizedReport: $(Split-Path -Leaf $optimizedReportPath) ($([math]::Round($reportSize, 2)) MB)" -ForegroundColor Gray
            
            # Set dynamic result file paths based on OptimizedReport filename
            $filePaths = Set-DynamicResultPaths -OptimizedReportPath $optimizedReportPath -ConfigurationName $ConfigurationName -OutputPath $outputPath
            $resultsPath = $filePaths.ResultsPath
            $excelReportPath = $filePaths.ExcelPath
        } else {
            throw "OptimizedReport JSON file was not found"
        }
    }
    catch {
        Write-Host "❌ Data collection failed: $($_.Exception.Message)" -ForegroundColor Red
        Write-Host "Please ensure you have proper permissions and connectivity to Microsoft Purview" -ForegroundColor Yellow
        exit 1
    }
} else {
    Write-Host "Step 1: Skipping data collection (using existing OptimizedReport)" -ForegroundColor Yellow
    
    # Get the latest OptimizedReport JSON file
    $optimizedReportPath = Get-LatestOptimizedReport -RunLogPath $runLogPath -OutputPath $outputPath
    
    if ($optimizedReportPath -and (Test-Path $optimizedReportPath)) {
        $reportSize = (Get-Item $optimizedReportPath).Length / 1MB
        Write-Host " Using OptimizedReport: $(Split-Path -Leaf $optimizedReportPath) ($([math]::Round($reportSize, 2)) MB)" -ForegroundColor Gray
        
        # Set dynamic result file paths based on OptimizedReport filename
        $filePaths = Set-DynamicResultPaths -OptimizedReportPath $optimizedReportPath -ConfigurationName $ConfigurationName -OutputPath $outputPath
        $resultsPath = $filePaths.ResultsPath
        $excelReportPath = $filePaths.ExcelPath
    } else {
        Write-Host "❌ No existing OptimizedReport JSON file found" -ForegroundColor Red
        Write-Host "Please run data collection first or check the run log at: $runLogPath" -ForegroundColor Yellow
        exit 1
    }
}

Write-Host ""

# Step 2: Validate Configuration Files
Write-Host "Step 2: Validating Configuration Files..." -ForegroundColor Yellow

if (-not (Test-Path $controlConfigPath)) {
    Write-Host "❌ Control configuration file not found: $controlConfigPath" -ForegroundColor Red
    Write-Host "Available configurations:" -ForegroundColor Yellow
    Get-ChildItem "$configBasePath\ControlBook_*_Config.csv" | ForEach-Object {
        $configName = $_.Name -replace "ControlBook_|_Config\.csv", ""
        Write-Host " - $configName" -ForegroundColor Gray
    }
    exit 1
}

if (-not (Test-Path $propertyConfigPath)) {
    Write-Host "❌ Property configuration file not found: $propertyConfigPath" -ForegroundColor Red
    exit 1
}

Write-Host "✅ Configuration files validated" -ForegroundColor Green
Write-Host " Controls: $controlConfigPath" -ForegroundColor Gray
Write-Host " Properties: $propertyConfigPath" -ForegroundColor Gray

Write-Host ""

# Step 3: Run Control Book Assessment
Write-Host "Step 3: Running Control Book Assessment..." -ForegroundColor Yellow

try {
    $assessmentResults = Test-ControlBook -ControlConfigPath $controlConfigPath -PropertyConfigPath $propertyConfigPath -OptimizedReportPath $optimizedReportPath -OutputPath $resultsPath -ConfigurationName $ConfigurationName
    
    Write-Host "✅ Assessment completed successfully" -ForegroundColor Green
    Write-Host " Results saved to: $resultsPath" -ForegroundColor Gray
}
catch {
    Write-Host "❌ Assessment failed: $($_.Exception.Message)" -ForegroundColor Red
    exit 1
}

Write-Host ""

# Step 4: Generate Excel Report (if requested)
if ($GenerateExcel) {
        # Always set Excel path to match CSV, just change extension to .xlsx
        $excelReportPath = [IO.Path]::ChangeExtension($resultsPath, '.xlsx')
        $excelDir = Split-Path $excelReportPath -Parent
        if (-not (Test-Path $excelDir)) {
            New-Item -Path $excelDir -ItemType Directory -Force | Out-Null
        }
        # Prepare Maturity Level Summary data - enriched with descriptions
        $maturitySummary = @()
        $maturityGroups = $assessmentResults.Results | Group-Object MaturityLevel
        foreach ($group in $maturityGroups) {
            $level = if ($group.Name -eq '' -or $null -eq $group.Name) { '(none)' } else { $group.Name }
            $total = $group.Group.Count
            $passing = ($group.Group | Where-Object { $_.Pass -eq $true }).Count
            $failing = $total - $passing
            $rate = if ($total -gt 0) { [math]::Round(($passing / $total) * 100, 1) } else { 0 }
            
            # Enhanced maturity level descriptions
            $description = switch ($level.ToLower()) {
                "1" { "Initial Stage - Basic security controls and foundational data protection measures" }
                "2" { "Intermediate Stage - Enhanced security policies with automated enforcement and monitoring" }
                "3" { "Advanced Stage - Comprehensive data security with AI-driven protection and full compliance" }
                "basic" { "Basic Level - Fundamental security controls implementation" }
                "intermediate" { "Intermediate Level - Enhanced security with policy automation" }
                "advanced" { "Advanced Level - Sophisticated security with intelligent protection" }
                "(none)" { "Unclassified - Controls without assigned maturity levels" }
                default { "Custom Level - Organization-specific maturity classification: $level" }
            }
            
            $status = if ($rate -ge 90) { "Excellent" } 
                     elseif ($rate -ge 80) { "Good" }
                     elseif ($rate -ge 70) { "Acceptable" }
                     elseif ($rate -ge 60) { "Needs Improvement" }
                     else { "Critical" }
            
            $maturitySummary += [PSCustomObject]@{
                'Maturity Level' = $level
                'Description' = $description
                'Total Controls' = $total
                'Passing Controls' = $passing
                'Failing Controls' = $failing
                'Compliance Rate %' = $rate
                'Status' = $status
                'Priority' = if ($level -match "^[123]$") { 
                    switch ($level) { "1" { "High - Foundation" }; "2" { "Medium - Enhancement" }; "3" { "Low - Optimization" } }
                } else { "Medium" }
            }
        }
    Write-Host "Step 4: Generating Excel Report..." -ForegroundColor Yellow
    try {
        # Check if ImportExcel module is available
        if (-not (Get-Module -ListAvailable -Name ImportExcel)) {
            Write-Host "⚠️ ImportExcel module not found. Installing..." -ForegroundColor Yellow
            Install-Module -Name ImportExcel -Force -Scope CurrentUser
        }
        Import-Module ImportExcel -Force
        # Create Excel workbook with multiple sheets
        $excelParams = @{
            Path = $excelReportPath
            AutoSize = $true
            AutoFilter = $true
            BoldTopRow = $true
            FreezeTopRow = $true
        }
        # Maturity Level Summary sheet (now after $excelParams is set)
        $maturitySummary | Export-Excel @excelParams -WorksheetName 'Maturity Level Summary'
        # Summary sheet
        $uniqueMaturityLevels = ($assessmentResults.Results | Where-Object { $_.MaturityLevel -ne $null -and $_.MaturityLevel -ne '' } | Select-Object -ExpandProperty MaturityLevel -Unique)
        $numMaturityLevels = ($uniqueMaturityLevels | Measure-Object).Count
        $summary = [PSCustomObject]@{
            "Assessment Date" = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
            "Configuration" = $ConfigurationName
            "Total Controls" = $assessmentResults.TotalControls
            "Passing Controls" = $assessmentResults.PassingControls
            "Failing Controls" = $assessmentResults.FailingControls
            "Compliance Rate %" = $assessmentResults.ComplianceRate
            "Data Source" = $optimizedReportPath
            "Maturity Levels Present" = if ($numMaturityLevels -gt 1) { $uniqueMaturityLevels -join ", " } else { $uniqueMaturityLevels }
        }
        $summary | Export-Excel @excelParams -WorksheetName "Summary"
        
        # Control Summary sheet - aggregated by Control ID
        $controlSummary = $assessmentResults.Results | Group-Object -Property ControlID | ForEach-Object {
            $controlGroup = $_.Group
            $controlPassed = $controlGroup | Where-Object { $_.Pass -eq $true }
            $controlFailed = $controlGroup | Where-Object { $_.Pass -eq $false }
            
            # A control is considered failed if ANY of its properties fail
            $overallResult = if ($controlFailed.Count -gt 0) { "Fail" } else { "Pass" }
            
            # Concatenate all comments from properties for this control
            $allComments = @()
            foreach ($property in $controlGroup) {
                if ($property.Comments -and $property.Comments.Trim() -ne "") {
                    $propertyComment = "$($property.Properties): $($property.Comments)"
                    $allComments += $propertyComment
                }
            }
            $combinedComments = if ($allComments.Count -gt 0) { $allComments -join " | " } else { "" }
            
            [PSCustomObject]@{
                "Capability" = $controlGroup[0].Capability
                "Control ID" = $controlGroup[0].ControlID
                "Control" = $controlGroup[0].Control
                "Maturity Level" = $controlGroup[0].MaturityLevel
                "Total Properties" = $controlGroup.Count
                "Properties Passed" = $controlPassed.Count
                "Properties Failed" = $controlFailed.Count
                "Result" = $overallResult
                "Comments" = $combinedComments
                "Configuration" = $controlGroup[0].ConfigurationName
            }
        }
        $controlSummary | Export-Excel @excelParams -WorksheetName "Control Summary"
        
        # Detailed results sheet with improved formatting (ensure MaturityLevel column is present)
        $detailedResults = $assessmentResults.Results | ForEach-Object {
            $_ | Add-Member -MemberType NoteProperty -Name "Result" -Value $(if ($_.Pass -eq $true) { "Pass" } else { "Fail" }) -Force
            $_ | Select-Object -Property * -ExcludeProperty Pass
        }
        if (-not ($detailedResults | Get-Member -Name 'MaturityLevel')) {
            $detailedResults = $detailedResults | Select-Object *, @{Name='MaturityLevel';Expression={''}}
        }
        $detailedResults | Export-Excel @excelParams -WorksheetName "Detailed Results"
        Write-Host "✅ Excel report generated successfully" -ForegroundColor Green
        Write-Host " Report saved to: $excelReportPath" -ForegroundColor Gray
    }
    catch {
        Write-Host "⚠️ Excel generation failed: $($_.Exception.Message)" -ForegroundColor Yellow
        Write-Host " CSV results are still available at: $resultsPath" -ForegroundColor Gray
    }
} else {
    Write-Host "Step 4: Skipping Excel report generation" -ForegroundColor Yellow
}

Write-Host ""

# Summary
Write-Host "=== Assessment Complete ===" -ForegroundColor Cyan
Write-Host "Configuration: $ConfigurationName" -ForegroundColor White
Write-Host "Compliance Rate: $($assessmentResults.ComplianceRate)%" -ForegroundColor $(if ($assessmentResults.ComplianceRate -ge 80) { "Green" } elseif ($assessmentResults.ComplianceRate -ge 60) { "Yellow" } else { "Red" })
Write-Host "Total Controls: $($assessmentResults.TotalControls)" -ForegroundColor White
Write-Host "Passing: $($assessmentResults.PassingControls)" -ForegroundColor Green
Write-Host "Failing: $($assessmentResults.FailingControls)" -ForegroundColor Red
$uniqueMaturityLevels = ($assessmentResults.Results | Where-Object { $_.MaturityLevel -ne $null -and $_.MaturityLevel -ne '' } | Select-Object -ExpandProperty MaturityLevel -Unique)
$numMaturityLevels = ($uniqueMaturityLevels | Measure-Object).Count
if ($numMaturityLevels -gt 1) {
    Write-Host ("Maturity Levels Present: " + ($uniqueMaturityLevels -join ", ")) -ForegroundColor White
} elseif ($numMaturityLevels -eq 1) {
    Write-Host ("Maturity Level: " + $uniqueMaturityLevels) -ForegroundColor White
} else {
    Write-Host "Maturity Level: (none)" -ForegroundColor White
}
Write-Host ""
Write-Host "Output Files:" -ForegroundColor White
Write-Host "- CSV Results: $resultsPath" -ForegroundColor Gray
if ($GenerateExcel -and (Test-Path $excelReportPath)) {
    Write-Host "- Excel Report: $excelReportPath" -ForegroundColor Gray
}
Write-Host ""
Write-Host "End Time: $(Get-Date)" -ForegroundColor White

# Print Maturity Level Summary in terminal
$maturityGroups = $assessmentResults.Results | Group-Object MaturityLevel
Write-Host "" -ForegroundColor White
Write-Host "Maturity Level Summary:" -ForegroundColor Cyan
Write-Host ("{0,-15} {1,15} {2,18} {3,16} {4,18}" -f 'Maturity Level','Total Controls','Passing Controls','Failing Controls','Compliance Rate %') -ForegroundColor White
foreach ($group in $maturityGroups) {
    $level = if ($group.Name -eq '' -or $null -eq $group.Name) { '(none)' } else { $group.Name }
    $total = $group.Group.Count
    $passing = ($group.Group | Where-Object { $_.Pass -eq $true }).Count
    $failing = $total - $passing
    $rate = if ($total -gt 0) { [math]::Round(($passing / $total) * 100, 1) } else { 0 }
    Write-Host ("{0,-15} {1,15} {2,18} {3,16} {4,18}" -f $level, $total, $passing, $failing, $rate) -ForegroundColor Gray
}

# Return results for potential further processing
return $assessmentResults