Private/New-DefaultNamedLocations.ps1

function New-DefaultNamedLocations {
    <#
    .SYNOPSIS
    Create default named locations for Conditional Access
     
    .DESCRIPTION
    Creates default named locations including trusted countries and blocked countries
    #>

    
    [CmdletBinding()]
    param()
    
    try {
        # Check if connected, if not connect using app registration
        $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. Please check your configuration."
                return
            }
            $context = Get-MgContext
        }
        
        Write-Host "Creating default named locations..." -ForegroundColor Yellow
        
        # Trusted countries (EU + trusted partners)
        $trustedCountries = @(
            "NL", "BE", "DE", "FR", "GB", "IE", "DK", "SE", "NO", "FI", 
            "AT", "CH", "LU", "ES", "PT", "IT", "GR", "PL", "CZ", "SK",
            "US", "CA", "AU", "NZ"
        )
        
        $trustedLocationName = "Trusted Countries - EU and Partners"
        
        # Check if location exists using REST API fallback
        $existingTrusted = $null
        try {
            $existingTrusted = Get-MgIdentityConditionalAccessNamedLocation -Filter "DisplayName eq '$trustedLocationName'" -Top 1 -ErrorAction SilentlyContinue
        }
        catch {
            # Use REST API fallback
            $invokeCmd = Get-Command Invoke-MgGraphRequest -ErrorAction SilentlyContinue
            if ($invokeCmd) {
                $allLocations = Invoke-MgGraphRequest -Method GET -Uri "https://graph.microsoft.com/v1.0/identity/conditionalAccess/namedLocations" -ErrorAction Stop
                $existingTrusted = $allLocations.value | Where-Object { $_.displayName -eq $trustedLocationName } | Select-Object -First 1
            }
        }
        
        if (-not $existingTrusted) {
            Write-Host " Creating: $trustedLocationName" -ForegroundColor Yellow
            $params = @{
                "@odata.type" = "#microsoft.graph.countryNamedLocation"
                displayName = $trustedLocationName
                countriesAndRegions = $trustedCountries
                includeUnknownCountriesAndRegions = $false
            }
            
            try {
                $trustedLocation = New-MgIdentityConditionalAccessNamedLocation -BodyParameter $params -ErrorAction Stop
                Write-Host " Created: $trustedLocationName (ID: $($trustedLocation.Id))" -ForegroundColor Green
            }
            catch {
                # Use REST API fallback - ensure correct JSON structure
                $invokeCmd = Get-Command Invoke-MgGraphRequest -ErrorAction SilentlyContinue
                if ($invokeCmd) {
                    # Build JSON body with correct property names (camelCase for REST API)
                    $body = @{
                        "@odata.type" = "#microsoft.graph.countryNamedLocation"
                        displayName = $trustedLocationName
                        countriesAndRegions = $trustedCountries
                        includeUnknownCountriesAndRegions = $false
                    } | ConvertTo-Json -Depth 5
                    
                    $trustedLocation = Invoke-MgGraphRequest -Method POST `
                        -Uri "https://graph.microsoft.com/v1.0/identity/conditionalAccess/namedLocations" `
                        -Body $body `
                        -ContentType "application/json" `
                        -ErrorAction Stop
                    Write-Host " Created: $trustedLocationName (ID: $($trustedLocation.id))" -ForegroundColor Green
                }
                else {
                    throw "Cannot create named location: $_"
                }
            }
        }
        else {
            Write-Host " Already exists: $trustedLocationName" -ForegroundColor Gray
        }
        
        # Blocked countries (high-risk)
        $blockedCountries = @(
            "CN", "RU", "KP", "IR", "SY", "CU", "SD", "VE"
        )
        
        $blockedLocationName = "Blocked Countries - High Risk"
        
        # Check if location exists using REST API fallback
        $existingBlocked = $null
        try {
            $existingBlocked = Get-MgIdentityConditionalAccessNamedLocation -Filter "DisplayName eq '$blockedLocationName'" -Top 1 -ErrorAction SilentlyContinue
        }
        catch {
            # Use REST API fallback
            $invokeCmd = Get-Command Invoke-MgGraphRequest -ErrorAction SilentlyContinue
            if ($invokeCmd) {
                if (-not $allLocations) {
                    $allLocations = Invoke-MgGraphRequest -Method GET -Uri "https://graph.microsoft.com/v1.0/identity/conditionalAccess/namedLocations" -ErrorAction Stop
                }
                $existingBlocked = $allLocations.value | Where-Object { $_.displayName -eq $blockedLocationName } | Select-Object -First 1
            }
        }
        
        if (-not $existingBlocked) {
            Write-Host " Creating: $blockedLocationName" -ForegroundColor Yellow
            $params = @{
                "@odata.type" = "#microsoft.graph.countryNamedLocation"
                displayName = $blockedLocationName
                countriesAndRegions = $blockedCountries
                includeUnknownCountriesAndRegions = $false
            }
            
            try {
                $blockedLocation = New-MgIdentityConditionalAccessNamedLocation -BodyParameter $params -ErrorAction Stop
                Write-Host " Created: $blockedLocationName (ID: $($blockedLocation.Id))" -ForegroundColor Green
            }
            catch {
                # Use REST API fallback - ensure correct JSON structure
                $invokeCmd = Get-Command Invoke-MgGraphRequest -ErrorAction SilentlyContinue
                if ($invokeCmd) {
                    # Build JSON body with correct property names (camelCase for REST API)
                    $body = @{
                        "@odata.type" = "#microsoft.graph.countryNamedLocation"
                        displayName = $blockedLocationName
                        countriesAndRegions = $blockedCountries
                        includeUnknownCountriesAndRegions = $false
                    } | ConvertTo-Json -Depth 5
                    
                    $blockedLocation = Invoke-MgGraphRequest -Method POST `
                        -Uri "https://graph.microsoft.com/v1.0/identity/conditionalAccess/namedLocations" `
                        -Body $body `
                        -ContentType "application/json" `
                        -ErrorAction Stop
                    Write-Host " Created: $blockedLocationName (ID: $($blockedLocation.id))" -ForegroundColor Green
                }
                else {
                    throw "Cannot create named location: $_"
                }
            }
        }
        else {
            Write-Host " Already exists: $blockedLocationName" -ForegroundColor Gray
        }
        
        Write-Host "Default named locations created." -ForegroundColor Green
    }
    catch {
        Write-Error "Failed to create named locations: $_"
    }
}