Public/Fix-NLBaselineCAPolicyNaming.ps1

function Fix-NLBaselineCAPolicyNaming {
    <#
    .SYNOPSIS
    Fix policy naming to match the standard naming convention
     
    .DESCRIPTION
    Identifies and renames policies that don't follow the CA###-{TargetGroup}-{Category}-{Apps}-{Platform}-{Control} naming convention.
    Matches policies based on their configuration and renames them to the correct baseline naming.
     
    .EXAMPLE
    Fix-NLBaselineCAPolicyNaming
     
    .EXAMPLE
    Fix-NLBaselineCAPolicyNaming -WhatIf
    #>

    
    [CmdletBinding(SupportsShouldProcess)]
    param(
        [switch]$WhatIf
    )
    
    try {
        # Check connection
        $context = Get-MgContext -ErrorAction SilentlyContinue
        if (-not $context -or -not $context.TenantId) {
            Write-Host "Not connected to Microsoft 365. Connecting..." -ForegroundColor Yellow
            Write-Host ""
            $connection = Connect-NLBaselineCA
            if (-not $connection) {
                Write-Error "Cannot connect to Microsoft 365"
                return
            }
        }
        
        Write-Host "========================================" -ForegroundColor Cyan
        Write-Host " FIX POLICY NAMING" -ForegroundColor Cyan
        Write-Host "========================================" -ForegroundColor Cyan
        Write-Host ""
        Write-Host "Scanning policies for incorrect naming..." -ForegroundColor Yellow
        Write-Host ""
        
        # Get all policies
        $policies = Get-AllConditionalAccessPolicies
        
        # Direct mapping of known incorrect names to correct names
        $directNameMappings = @{
            "GLOBAL - 1050 - BLOCK - High-Risk Countries" = "CA007-Global-AttackSurfaceReduction-AnyApp-AnyPlatform-BLOCK-HighRiskCountries"
            "GLOBAL - 2040 - GRANT - Terms of Use (All users)" = "CA008-Global-Compliance-AnyApp-AnyPlatform-TermsOfUse"
            "GLOBAL - 2056 - GRANT - MFA for Windows Azure Service Management API" = "CA013-Global-IdentityProtection-AzureManagement-AnyPlatform-MFA"
            "GLOBAL - 2057 - GRANT - MFA for Microsoft Admin Portals" = "CA014-Global-IdentityProtection-AdminPortals-AnyPlatform-MFA"
            "GLOBAL - 2060 - GRANT - Mobile Apps and Desktop Clients" = "CA011-Global-BaseProtection-MobileDesktop-AnyPlatform-CompliantDevice"
        }
        
        if (-not $policies -or $policies.Count -eq 0) {
            Write-Host "No policies found." -ForegroundColor Yellow
            return
        }
        
        # Define mapping of old naming patterns to correct naming
        # Match exact display names that the user reported
        $namingMappings = @(
            @{
                Pattern = "^GLOBAL\s*-\s*1050\s*-\s*BLOCK\s*-\s*High-Risk Countries$|^GLOBAL\s*-\s*\d+\s*-\s*BLOCK\s*-\s*High-Risk"
                CorrectName = "CA007-Global-AttackSurfaceReduction-AnyApp-AnyPlatform-BLOCK-HighRiskCountries"
                MatchFunction = {
                    param($policy)
                    # Match policies that block high-risk countries
                    $policy.grantControls.builtInControls -contains "block" -and
                    $policy.conditions.locations -and
                    $policy.conditions.locations.excludeLocations -and
                    $policy.conditions.locations.excludeLocations.Count -gt 0
                }
            },
            @{
                Pattern = "^GLOBAL\s*-\s*2040\s*-\s*GRANT\s*-\s*Terms of Use|^GLOBAL\s*-\s*\d+\s*-\s*GRANT\s*-\s*Terms of Use"
                CorrectName = "CA008-Global-Compliance-AnyApp-AnyPlatform-TermsOfUse"
                MatchFunction = {
                    param($policy)
                    # Match policies that require Terms of Use
                    ($policy.grantControls.builtInControls -contains "termsOfUse") -or
                    ($policy.grantControls.termsOfUse -and $policy.grantControls.termsOfUse.Count -gt 0)
                }
            },
            @{
                Pattern = "^GLOBAL\s*-\s*2056\s*-\s*GRANT\s*-\s*MFA for Windows Azure Service Management|^GLOBAL\s*-\s*\d+\s*-\s*GRANT\s*-\s*.*Azure.*Service.*Management"
                CorrectName = "CA013-Global-IdentityProtection-AzureManagement-AnyPlatform-MFA"
                MatchFunction = {
                    param($policy)
                    # Match policies for Azure Management API (Windows Azure Service Management)
                    $azureMgmtAppId = "797f4846-ba00-4fd7-ba43-dac1f8f63013"
                    ($policy.conditions.applications.includeApplications -contains $azureMgmtAppId) -or
                    ($policy.conditions.applications.includeApplications -contains "797f4846-ba00-4fd7-ba43-5acf33b5e2c5") -or
                    ($policy.conditions.applications.includeApplications -contains "AzureManagement")
                }
            },
            @{
                Pattern = "^GLOBAL\s*-\s*2057\s*-\s*GRANT\s*-\s*MFA for Microsoft Admin Portals|^GLOBAL\s*-\s*\d+\s*-\s*GRANT\s*-\s*.*Admin Portals"
                CorrectName = "CA014-Global-IdentityProtection-AdminPortals-AnyPlatform-MFA"
                MatchFunction = {
                    param($policy)
                    # Match policies for Microsoft Admin Portals
                    ($policy.conditions.applications.includeApplications -contains "MicrosoftAdminPortals") -or
                    ($policy.conditions.applications.includeApplications -contains "Office365" -and
                     $policy.grantControls.builtInControls -contains "mfa")
                }
            },
            @{
                Pattern = "^GLOBAL\s*-\s*2060\s*-\s*GRANT\s*-\s*Mobile Apps and Desktop Clients|^GLOBAL\s*-\s*\d+\s*-\s*GRANT\s*-\s*.*Mobile.*Desktop"
                CorrectName = "CA011-Global-BaseProtection-MobileDesktop-AnyPlatform-CompliantDevice"
                MatchFunction = {
                    param($policy)
                    # Match policies for Mobile Apps and Desktop Clients with device compliance
                    ($policy.conditions.clientAppTypes -contains "mobileAppsAndDesktopClients") -and
                    ($policy.grantControls.builtInControls -contains "compliantDevice" -or
                     $policy.grantControls.builtInControls -contains "domainJoinedDevice" -or
                     $policy.grantControls.builtInControls -contains "hybridAzureADJoinedDevice")
                }
            }
        )
        
        # Also check for policies that match old naming patterns
        $policiesToRename = @()
        
        foreach ($policy in $policies) {
            $currentName = $policy.DisplayName
            
            # Check if policy already follows naming convention
            if ($currentName -match "^CA\d{3}-") {
                continue
            }
            
            # First check direct name mappings (exact match)
            $matched = $false
            if ($directNameMappings.ContainsKey($currentName)) {
                $policiesToRename += @{
                    Policy = $policy
                    OldName = $currentName
                    NewName = $directNameMappings[$currentName]
                    Reason = "Direct name mapping"
                }
                $matched = $true
            }
            
            # If not matched, try pattern matching
            if (-not $matched) {
                foreach ($mapping in $namingMappings) {
                    # Try both case-sensitive and case-insensitive matching
                    if ($currentName -match $mapping.Pattern -or $currentName -imatch $mapping.Pattern) {
                        $policiesToRename += @{
                            Policy = $policy
                            OldName = $currentName
                            NewName = $mapping.CorrectName
                            Reason = "Matched naming pattern"
                        }
                        $matched = $true
                        break
                    }
                }
            }
            
            # If not matched by pattern, try to match by configuration
            if (-not $matched) {
                foreach ($mapping in $namingMappings) {
                    try {
                        $matchResult = & $mapping.MatchFunction $policy
                        if ($matchResult) {
                            # Check if we haven't already added this policy
                            $alreadyAdded = $policiesToRename | Where-Object { $_.Policy.Id -eq $policy.Id }
                            if (-not $alreadyAdded) {
                                $policiesToRename += @{
                                    Policy = $policy
                                    OldName = $currentName
                                    NewName = $mapping.CorrectName
                                    Reason = "Matched by policy configuration"
                                }
                                $matched = $true
                                break
                            }
                        }
                    }
                    catch {
                        # Skip if matching function fails
                    }
                }
            }
        }
        
        if ($policiesToRename.Count -eq 0) {
            Write-Host "No policies found with incorrect naming." -ForegroundColor Green
            Write-Host ""
            return
        }
        
        Write-Host "Found $($policiesToRename.Count) policies with incorrect naming:" -ForegroundColor Yellow
        Write-Host ""
        
        foreach ($item in $policiesToRename) {
            Write-Host "Policy: $($item.OldName)" -ForegroundColor White
            Write-Host " Will be renamed to: $($item.NewName)" -ForegroundColor Cyan
            Write-Host " Reason: $($item.Reason)" -ForegroundColor Gray
            Write-Host ""
        }
        
        if ($WhatIf) {
            Write-Host "WhatIf: No changes will be made." -ForegroundColor Yellow
            return
        }
        
        $confirm = Read-Host "Do you want to rename these policies? (Y/N)"
        if ($confirm -ne "Y" -and $confirm -ne "y") {
            Write-Host "Operation cancelled" -ForegroundColor Yellow
            return
        }
        
        $renamedCount = 0
        $errors = @()
        
        foreach ($item in $policiesToRename) {
            try {
                if ($PSCmdlet.ShouldProcess($item.OldName, "Rename to $($item.NewName)")) {
                    Write-Host "Renaming: $($item.OldName) -> $($item.NewName)" -ForegroundColor Yellow
                    
                    # Check if new name already exists
                    $existingPolicy = $policies | Where-Object { $_.DisplayName -eq $item.NewName -and $_.Id -ne $item.Policy.Id }
                    if ($existingPolicy) {
                        Write-Host " Warning: Policy with name '$($item.NewName)' already exists. Skipping." -ForegroundColor Yellow
                        $errors += "Policy '$($item.OldName)' - target name '$($item.NewName)' already exists"
                        continue
                    }
                    
                    # Update policy using REST API
                    $body = @{
                        displayName = $item.NewName
                    } | ConvertTo-Json -Depth 10
                    
                    $invokeCmd = Get-Command Invoke-MgGraphRequest -ErrorAction SilentlyContinue
                    if ($invokeCmd) {
                        Invoke-MgGraphRequest -Method PATCH `
                            -Uri "https://graph.microsoft.com/v1.0/identity/conditionalAccess/policies/$($item.Policy.Id)" `
                            -Body $body `
                            -ContentType "application/json" `
                            -ErrorAction Stop
                        
                        Write-Host " Renamed successfully" -ForegroundColor Green
                        $renamedCount++
                    }
                    else {
                        throw "Invoke-MgGraphRequest not available"
                    }
                }
            }
            catch {
                $errors += "Error renaming $($item.OldName): $_"
                Write-Host " Error: $_" -ForegroundColor Red
            }
        }
        
        Write-Host ""
        Write-Host "========================================" -ForegroundColor Green
        Write-Host " SUMMARY" -ForegroundColor Green
        Write-Host "========================================" -ForegroundColor Green
        Write-Host "Renamed: $renamedCount policies" -ForegroundColor White
        if ($errors.Count -gt 0) {
            Write-Host "Errors: $($errors.Count)" -ForegroundColor Red
            foreach ($error in $errors) {
                Write-Host " - $error" -ForegroundColor Yellow
            }
        }
        Write-Host ""
    }
    catch {
        Write-Error "Error fixing policy naming: $_"
    }
}