diff --git a/03-Azure/01-03-Infrastructure/01_Sovereign_Cloud/challenges/challenge-01.md b/03-Azure/01-03-Infrastructure/01_Sovereign_Cloud/challenges/challenge-01.md index a20fb3887..c7ba127ee 100644 --- a/03-Azure/01-03-Infrastructure/01_Sovereign_Cloud/challenges/challenge-01.md +++ b/03-Azure/01-03-Infrastructure/01_Sovereign_Cloud/challenges/challenge-01.md @@ -10,7 +10,7 @@ The goal of this exercise is to establish foundational sovereign cloud governanc ## Actions -- Create and assign Azure Policy controls to restrict deployments to the lab-approved European regions (Norway East, Germany North, North Europe, West Europe). West Europe accommodates Azure Local management resources when LocalBox is registered there. +- Create and assign Azure Policy controls using the lab-approved European regions (Norway East, Germany North, North Europe, West Europe), plus **Australia East as a lab-only exception** for Azure Local management resources. This exception is not a European data-residency recommendation; keep the exercise assignments in **DoNotEnforce** mode. - Enforce resource tagging requirements for data classification and compliance tracking. - Block public IP resource creation and evaluate storage public-network-access restrictions. Disabling public network access does not create or verify a private endpoint. - Assign least-privilege RBAC roles for the SovereignOps team. diff --git a/03-Azure/01-03-Infrastructure/01_Sovereign_Cloud/labautomation/deploy-localbox.ps1 b/03-Azure/01-03-Infrastructure/01_Sovereign_Cloud/labautomation/deploy-localbox.ps1 index 97b928933..e89a08685 100644 --- a/03-Azure/01-03-Infrastructure/01_Sovereign_Cloud/labautomation/deploy-localbox.ps1 +++ b/03-Azure/01-03-Infrastructure/01_Sovereign_Cloud/labautomation/deploy-localbox.ps1 @@ -26,7 +26,8 @@ Optional tenant-specific object ID of the Microsoft.AzureStackHCI enterprise application. The script resolves it through Microsoft Graph when omitted. .PARAMETER AzureLocalInstanceLocation Azure Local registration region, separate from the Azure host region. -Defaults to West Europe to align with the Challenge 1 location allowlist. +Defaults to Australia East for the lab. The upstream staging storage account +also uses this region, independently of the Azure host region. .PARAMETER NoWait Submit the deployment without waiting for ARM completion. #> @@ -57,7 +58,7 @@ param( [string]$AzureLocalResourceProviderObjectId, [ValidateSet('australiaeast', 'southcentralus', 'eastus', 'westeurope', 'southeastasia', 'canadacentral', 'japaneast', 'centralindia')] - [string]$AzureLocalInstanceLocation = 'westeurope', + [string]$AzureLocalInstanceLocation = 'australiaeast', [switch]$NoWait ) @@ -68,14 +69,24 @@ $PSNativeCommandUseErrorActionPreference = $true function Invoke-AzJson { param([Parameter(Mandatory = $true)][string[]]$Arguments) - $output = & az @Arguments --only-show-errors --output json - if ($LASTEXITCODE -ne 0) { - throw "Azure CLI command failed: az $($Arguments -join ' ')" + # Preserve Azure's error details for the caller's regional fallback classifier. + $PSNativeCommandUseErrorActionPreference = $false + $output = @(& az @Arguments --only-show-errors --output json 2>&1) + $exitCode = $LASTEXITCODE + $stderr = @($output | Where-Object { $_ -is [System.Management.Automation.ErrorRecord] }) + $stdout = ($output | Where-Object { $_ -isnot [System.Management.Automation.ErrorRecord] }) -join "`n" + $operation = ($Arguments | Select-Object -First 3) -join ' ' + if ($exitCode -ne 0) { + throw "Azure CLI 'az $operation' failed (exit code ${exitCode}): $($stderr -join "`n")" } - if ([string]::IsNullOrWhiteSpace(($output -join "`n"))) { + foreach ($message in $stderr) { Write-Warning "$message" } + if ([string]::IsNullOrWhiteSpace($stdout)) { return $null } - return ($output -join "`n") | ConvertFrom-Json + try { return $stdout | ConvertFrom-Json -ErrorAction Stop } + catch { + throw "Azure CLI 'az $operation' returned invalid JSON. Raw stdout and command arguments are omitted because they may contain sensitive data. Inspect deployment state before retrying." + } } function New-LocalBoxPassword { diff --git a/03-Azure/01-03-Infrastructure/01_Sovereign_Cloud/labautomation/shared-deploy-lab.ps1 b/03-Azure/01-03-Infrastructure/01_Sovereign_Cloud/labautomation/shared-deploy-lab.ps1 index 65441eb90..dce4e831d 100644 --- a/03-Azure/01-03-Infrastructure/01_Sovereign_Cloud/labautomation/shared-deploy-lab.ps1 +++ b/03-Azure/01-03-Infrastructure/01_Sovereign_Cloud/labautomation/shared-deploy-lab.ps1 @@ -213,7 +213,7 @@ else { -ResourceGroupName $localBoxResourceGroupName ` -Location $localBoxLocation ` -AzureLocalResourceProviderObjectId $azureLocalResourceProviderObjectIds[0] ` - -AzureLocalInstanceLocation 'westeurope' ` + -AzureLocalInstanceLocation 'australiaeast' ` -UseConsoleCredentials ` -NoWait diff --git a/03-Azure/01-03-Infrastructure/01_Sovereign_Cloud/resources/hosted-events/readme.md b/03-Azure/01-03-Infrastructure/01_Sovereign_Cloud/resources/hosted-events/readme.md index 389c5634f..9c5aa827e 100644 --- a/03-Azure/01-03-Infrastructure/01_Sovereign_Cloud/resources/hosted-events/readme.md +++ b/03-Azure/01-03-Infrastructure/01_Sovereign_Cloud/resources/hosted-events/readme.md @@ -37,15 +37,34 @@ organizer's post-provisioning preparation. ### LocalBox registration region and location policies +If LocalBox validation reports `RequestDisallowedByAzure` with +`locationineligible` ("the selected region is currently not accepting new +customers"), identify the rejected resource and its actual region before +changing the host-region selection. This is distinct from an exercise policy's +`RequestDisallowedByPolicy`. The upstream +[LocalBox template](https://github.com/microsoft/azure_arc/blob/main/azure_jumpstart_localbox/bicep/main.bicep) +passes `azureLocalInstanceLocation` to the staging storage module, whose account +name is `localbox` plus a generated suffix. In the 4 October 2026 hosted test, +this account was rejected in West Europe while the host was selected in Spain +Central. **Host-region fallback cannot resolve a rejection in the fixed +registration/staging-storage region.** The shared setup tries +the next configured preferred host region for recognized regional failures; +it does not change the Azure Local registration region or delete the shared +resource group. The CLI wrapper retains Azure's error details in the exception +so PowerShell's generic native-command exit error cannot hide them from the +fallback check. Policy denials, authorization failures, and unrecognized errors +still stop the run. If all preferred regions fail, the final error is reported; +do not assume a region is available merely because its SKU/quota checks passed. + There are three separate locations to check: the participant resource group's metadata location, the Azure region hosting the LocalBox simulator, and the Azure Local/custom-location registration region. The hosted [shared hook](../../labautomation/shared-deploy-lab.ps1) explicitly passes -`AzureLocalInstanceLocation = westeurope`; the +`AzureLocalInstanceLocation = australiaeast`; the [manual deployment](../manual-setup/localbox/deploy-localbox.ps1) defaults to -`westeurope`. Host-region fallback does not change that registration parameter. -The hosted deployer's default also matches West Europe. This changes registration -for fresh deployments, not the Azure host-region selection. Inspect the deployed +`australiaeast`. Host-region fallback does not change that registration parameter. +The hosted deployer's default also matches Australia East. This changes registration +and staging storage for fresh deployments, not the Azure host-region selection. Inspect the deployed custom location's **JSON View** for its actual `location`. Azure Local VM management resources use the custom location's region even when @@ -64,16 +83,17 @@ exception. Do not disable unrelated policies or automatically broaden the subscription allowlist. The hosted control tags below do not override arbitrary location-deny policies. -[Microsoft's current Azure Local region list](https://learn.microsoft.com/azure/azure-local/concepts/system-requirements-23h2#azure-requirements) -includes West Europe as its only European region for hyperconverged deployments. -The current Challenge 1 exercise allowlists include West Europe, but older -assignments may still use the original three-region list. This does not prove -that West Europe is allowed by the event subscription's inherited policies, and -it does not permit Australia East. Existing Azure assignments are not updated -automatically. Earlier hosted test environments registered in Australia East -are not relocated by this change; validate the next event using a fresh deployment -from the updated content. Existing-environment cleanup is a separate organizer -action, not part of the registration-region change. +The lab returns to Australia East after this West Europe rejection; this is +not evidence that West Europe is universally unavailable for Azure Local. +[Service region support](https://learn.microsoft.com/azure/azure-local/concepts/system-requirements-23h2#azure-requirements) +does not guarantee subscription eligibility for every dependent resource. +Challenge 1 retains its four European regions and adds Australia East as an +explicit **lab-only exception**, not a European data-residency recommendation. +Earlier assignments must be updated by their owners; inherited policies may +still deny the region, and control tags do not bypass them. Existing Azure +assignments and resources are not updated or relocated automatically. Validate +the next event using a fresh deployment and the participant VM exercise. +Existing-environment cleanup is a separate organizer action. ### Hosted MCAPS control-tag initiative diff --git a/03-Azure/01-03-Infrastructure/01_Sovereign_Cloud/resources/manual-setup/localbox/deploy-localbox.ps1 b/03-Azure/01-03-Infrastructure/01_Sovereign_Cloud/resources/manual-setup/localbox/deploy-localbox.ps1 index d495de396..68d8e823d 100644 --- a/03-Azure/01-03-Infrastructure/01_Sovereign_Cloud/resources/manual-setup/localbox/deploy-localbox.ps1 +++ b/03-Azure/01-03-Infrastructure/01_Sovereign_Cloud/resources/manual-setup/localbox/deploy-localbox.ps1 @@ -24,6 +24,10 @@ .PARAMETER Location Azure region for deployment (default: swedencentral) +.PARAMETER AzureLocalInstanceLocation + Azure Local registration and staging storage region (default: australiaeast). + This lab default is independent of the Azure host region. + .PARAMETER WindowsAdminUsername Admin username for Windows VMs (default: arcdemo) @@ -80,7 +84,7 @@ param( [Parameter(Mandatory = $false)] [ValidateSet('australiaeast', 'southcentralus', 'eastus', 'westeurope', 'southeastasia', 'canadacentral', 'japaneast', 'centralindia')] - [string]$AzureLocalInstanceLocation = "westeurope" + [string]$AzureLocalInstanceLocation = "australiaeast" ) Write-Host "`n=== Azure Arc Jumpstart LocalBox Deployment ===" -ForegroundColor Cyan diff --git a/03-Azure/01-03-Infrastructure/01_Sovereign_Cloud/resources/manual-setup/readme.md b/03-Azure/01-03-Infrastructure/01_Sovereign_Cloud/resources/manual-setup/readme.md index 21a57c85e..af04ff76f 100644 --- a/03-Azure/01-03-Infrastructure/01_Sovereign_Cloud/resources/manual-setup/readme.md +++ b/03-Azure/01-03-Infrastructure/01_Sovereign_Cloud/resources/manual-setup/readme.md @@ -26,7 +26,7 @@ Run the [manual LocalBox entry point](localbox/deploy-localbox.ps1) from an auth ./localbox/deploy-localbox.ps1 -ResourceGroupName 'rg-localbox-shared' -Location 'swedencentral' ``` -Choose an allowed Azure Local registration region with `-AzureLocalInstanceLocation` when needed. It can differ from the Azure host region. Wait for the nested Azure Local deployment, then connect to `LocalBox-Client` and follow the shared [post-provisioning guide](../localbox/readme.md). Supply an existing Entra security-group object ID for AKS; its intended administrators must be members. +The Azure Local registration and staging storage region defaults to **Australia East** for this lab, independently of the Azure host region. Choose another supported and subscription-eligible region with `-AzureLocalInstanceLocation` when needed; this lab default is not a European data-residency recommendation. Wait for the nested Azure Local deployment, then connect to `LocalBox-Client` and follow the shared [post-provisioning guide](../localbox/readme.md). Supply an existing Entra security-group object ID for AKS; its intended administrators must be members. Run the [health checks](../tests/readme.md) before participants begin. Verify scoped participant access and approved Defender for Servers settings using the [manual preparation and readiness reference](../localbox/manual-preparation.md). diff --git a/03-Azure/01-03-Infrastructure/01_Sovereign_Cloud/resources/tests/prepare-localbox.tests.ps1 b/03-Azure/01-03-Infrastructure/01_Sovereign_Cloud/resources/tests/prepare-localbox.tests.ps1 index d24e3b9ff..c0eb85de1 100644 --- a/03-Azure/01-03-Infrastructure/01_Sovereign_Cloud/resources/tests/prepare-localbox.tests.ps1 +++ b/03-Azure/01-03-Infrastructure/01_Sovereign_Cloud/resources/tests/prepare-localbox.tests.ps1 @@ -699,7 +699,7 @@ Describe 'Console LocalBox credential isolation' { } Describe 'LocalBox registration region contract' { - It 'defaults to West Europe independently of the host region' -TestCases @( + It 'defaults to Australia East independently of the host region' -TestCases @( @{ Path = 'labautomation/deploy-localbox.ps1' } @{ Path = 'resources/manual-setup/localbox/deploy-localbox.ps1' } ) { @@ -709,20 +709,20 @@ Describe 'LocalBox registration region contract' { "$PSScriptRoot/../../$Path", [ref]$null, [ref]$errors) $errors.Count | Should -Be 0 $registration = $ast.ParamBlock.Parameters | Where-Object { $_.Name.VariablePath.UserPath -eq 'AzureLocalInstanceLocation' } - $registration.DefaultValue.SafeGetValue() | Should -Be 'westeurope' + $registration.DefaultValue.SafeGetValue() | Should -Be 'australiaeast' $hostRegion = $ast.ParamBlock.Parameters | Where-Object { $_.Name.VariablePath.UserPath -eq 'Location' } $hostRegion.DefaultValue.SafeGetValue() | Should -Be 'swedencentral' $ast.Extent.Text | Should -Match 'azureLocalInstanceLocation\s*=\s*@\{\s*value\s*=\s*\$AzureLocalInstanceLocation\s*\}' $ast.Extent.Text | Should -Match 'location\s*=\s*@\{\s*value\s*=\s*\$Location\s*\}' } - It 'uses West Europe registration while preserving shared host-region selection' { + It 'uses Australia East registration while preserving shared host-region selection' { $errors = $null $ast = [System.Management.Automation.Language.Parser]::ParseFile( "$PSScriptRoot/../../labautomation/shared-deploy-lab.ps1", [ref]$null, [ref]$errors) $errors.Count | Should -Be 0 - $ast.Extent.Text | Should -Match "-AzureLocalInstanceLocation 'westeurope'" + $ast.Extent.Text | Should -Match "-AzureLocalInstanceLocation 'australiaeast'" $ast.Extent.Text | Should -Match '-Location \$localBoxLocation' - $ast.Extent.Text | Should -Not -Match "-AzureLocalInstanceLocation 'australiaeast'" + $ast.Extent.Text | Should -Not -Match "-AzureLocalInstanceLocation 'westeurope'" } It 'includes the registration region in all four Challenge 1 policy parameter lists' { $guide = Get-Content "$PSScriptRoot/../../walkthrough/challenge-01/solution-01.md" -Raw @@ -730,11 +730,108 @@ Describe 'LocalBox registration region contract' { $lists.Count | Should -Be 4 foreach ($list in $lists) { $regions = @($list.Groups[1].Value | ConvertFrom-Json) - $regions.Count | Should -Be 4 - foreach ($region in @('norwayeast', 'germanynorth', 'northeurope', 'westeurope')) { + $regions.Count | Should -Be 5 + foreach ($region in @('norwayeast', 'germanynorth', 'northeurope', 'westeurope', 'australiaeast')) { $regions | Should -Contain $region } - $regions | Should -Not -Contain 'australiaeast' + } + } +} + +Describe 'Console LocalBox CLI failure propagation' { + BeforeAll { + $deployer = [Management.Automation.Language.Parser]::ParseFile( + "$PSScriptRoot/../../labautomation/deploy-localbox.ps1", [ref]$null, [ref]$null) + $helper = $deployer.Find({ + param($node) + $node -is [Management.Automation.Language.FunctionDefinitionAst] -and $node.Name -eq 'Invoke-AzJson' + }, $true) + . ([scriptblock]::Create($helper.Extent.Text)) + $shared = [Management.Automation.Language.Parser]::ParseFile( + "$PSScriptRoot/../../labautomation/shared-deploy-lab.ps1", [ref]$null, [ref]$null) + $loop = $shared.Find({ + param($node) + $node -is [Management.Automation.Language.ForEachStatementAst] -and + $node.Variable.Extent.Text -eq '$localBoxLocation' + }, $true) + $fallback = Join-Path $TestDrive 'fallback.ps1' + $loop.Extent.Text | Set-Content $fallback + $nativeExecutable = (Get-Process -Id $PID).Path + function az { & $nativeExecutable -NoProfile -NonInteractive -Command $cliState.Command } + } + BeforeEach { + $ErrorActionPreference = 'Stop' + $PSNativeCommandUseErrorActionPreference = $true + $cliState = @{ Command = '[Console]::Error.WriteLine("RequestDisallowedByAzure: locationineligible"); exit 1'; Attempts = 0 } + } + It 'retains regional error details with native error preference ' -ForEach @( + @{ Preference = $true } + @{ Preference = $false } + ) { + $PSNativeCommandUseErrorActionPreference = $Preference + { Invoke-AzJson @('deployment', 'group', 'validate', '--parameters', 'mock-secret') } | + Should -Throw '*exit code 1*RequestDisallowedByAzure*locationineligible*' + $PSNativeCommandUseErrorActionPreference | Should -Be $Preference + } + It 'parses successful JSON without mixing in native stderr' { + $cliState.Command = '[Console]::Error.WriteLine("notice"); [Console]::Out.WriteLine(''{"state":"ready"}''); exit 0' + Mock Write-Warning {} + (Invoke-AzJson @('account', 'show')).state | Should -Be 'ready' + Should -Invoke Write-Warning -Times 1 -Exactly -ParameterFilter { $Message -eq 'notice' } + } + It 'preserves empty successful output' { + $cliState.Command = 'exit 0' + Invoke-AzJson @('account', 'set') | Should -BeNullOrEmpty + } + It 'does not print command arguments or invalid JSON stdout on failure' { + $cliState.Command = '[Console]::Out.WriteLine("mock-sensitive-output"); exit 0' + $failure = try { Invoke-AzJson @('deployment', 'group', 'create', '--parameters', 'mock-secret') } catch { $_ } + $failure.Exception.Message | Should -Match 'invalid JSON' + $failure.Exception.Message | Should -Not -Match 'mock-sensitive-output|mock-secret' + } + It 'does not accept valid JSON from a command that failed' { + $cliState.Command = '[Console]::Out.WriteLine(''{"state":"ready"}''); exit 17' + { Invoke-AzJson @('deployment', 'group', 'validate') } | Should -Throw '*exit code 17*' + } + Context 'Shared region loop' { + BeforeEach { + @' +param($Location) +Invoke-AzJson @('deployment', 'group', 'validate', '--location', $Location) | Out-Null +[pscustomobject]@{ ProvisioningState = 'Submitted'; Location = $Location } +'@ | Set-Content (Join-Path $TestDrive 'deploy-localbox.ps1') + $localBoxLocations = @('first-region', 'second-region') + $azureLocalResourceProviderObjectIds = @('test-object-id') + $localBoxDeployment = $null + Mock az { + $cliState.Attempts++ + if ($cliState.Attempts -eq 2) { + & $nativeExecutable -NoProfile -NonInteractive -Command 'exit 0' + } + else { + & $nativeExecutable -NoProfile -NonInteractive -Command $cliState.Command + } + } + } + It 'tries the next region after the actual native regional failure' { + . $fallback + $localBoxDeployment.ProvisioningState | Should -Be 'Submitted' + $localBoxDeployment.Location | Should -Be 'second-region' + Should -Invoke az -Times 2 -Exactly + } + It 'stops without retrying ' -ForEach @( + @{ Code = 'RequestDisallowedByPolicy' } + @{ Code = 'AuthorizationFailed' } + @{ Code = 'UnexpectedFailure' } + ) { + $cliState.Command = "[Console]::Error.WriteLine('$Code'); exit 1" + { . $fallback } | Should -Throw "*$Code*" + Should -Invoke az -Times 1 -Exactly + } + It 'preserves the regional failure when no fallback regions remain' { + $localBoxLocations = @('first-region') + { . $fallback } | Should -Throw '*RequestDisallowedByAzure*locationineligible*' + Should -Invoke az -Times 1 -Exactly } } } diff --git a/03-Azure/01-03-Infrastructure/01_Sovereign_Cloud/resources/tests/readme.md b/03-Azure/01-03-Infrastructure/01_Sovereign_Cloud/resources/tests/readme.md index 466b347a5..d226a5c14 100644 --- a/03-Azure/01-03-Infrastructure/01_Sovereign_Cloud/resources/tests/readme.md +++ b/03-Azure/01-03-Infrastructure/01_Sovereign_Cloud/resources/tests/readme.md @@ -33,9 +33,10 @@ authorized test of real network access through the load-balancer address. The LocalBox registration-region contract is checked offline with `Invoke-Pester ./prepare-localbox.tests.ps1 -FullName 'LocalBox registration region contract*' -Output Detailed`. -It verifies that both deployment entry points default registration to West Europe, +It verifies that both deployment entry points default registration to Australia East, the shared hook passes that region independently of host-region selection, and -all four Challenge 1 policy parameter examples include it. These checks make no +all four Challenge 1 policy parameter examples include the Australia East lab-only +exception while retaining the four European regions. These checks make no Azure changes; a fresh Console deployment and participant VM creation remain required to validate regional service availability and effective inherited policies. diff --git a/03-Azure/01-03-Infrastructure/01_Sovereign_Cloud/walkthrough/challenge-01/solution-01.md b/03-Azure/01-03-Infrastructure/01_Sovereign_Cloud/walkthrough/challenge-01/solution-01.md index 08cca1006..b991d42cf 100644 --- a/03-Azure/01-03-Infrastructure/01_Sovereign_Cloud/walkthrough/challenge-01/solution-01.md +++ b/03-Azure/01-03-Infrastructure/01_Sovereign_Cloud/walkthrough/challenge-01/solution-01.md @@ -144,7 +144,7 @@ az policy definition show --name e56962a6-4747-49cd-b67b-bf8b01975c4c \ - **Exclusions**: Leave empty - **Policy definition**: Search for "Allowed locations" - **Assignment name**: Use the format "Lab User-{YourAttendeeNumber} - Restrict to Sovereign Regions" (e.g., "Lab User-0024 - Restrict to Sovereign Regions") - - **Description**: "Restrict all resource deployments to EU sovereign regions for data residency compliance" + - **Description**: "Demonstrate location controls using approved European regions plus an Australia East lab-only exception for Azure Local" - **Policy enforcement**: **Do not enforce** 5. Click **Next** to go to **Parameters** @@ -153,11 +153,12 @@ az policy definition show --name e56962a6-4747-49cd-b67b-bf8b01975c4c \ - Germany North - North Europe - West Europe + - Australia East (lab-only exception) 7. Click **Review + create** and then **Create** -West Europe is included to accommodate Azure Local management resources in Challenge 6 when the shared LocalBox is registered there, reducing location-policy conflicts if enforcement is accidentally left enabled. It does not authorize Australia East or relocate an existing LocalBox registration. Keep **Do not enforce** and your **own resource-group scope** as instructed; other exercise policies can still block later challenges if left enforcing. +West Europe remains in the European allowlist. **Australia East is an explicit lab-only exception** for Challenge 6 Azure Local management resources, reducing location-policy conflicts if enforcement is accidentally left enabled. Fresh LocalBox deployments register there and place their staging storage account there after the hosted test encountered a West Europe eligibility rejection. This exception is **not** a European data-residency recommendation and does not relocate existing resources. Keep **Do not enforce** and your **own resource-group scope** as instructed; other exercise policies can still block later challenges if left enforcing. -If you already created your own exercise assignment using the earlier three-region list, use **Edit assignment > Parameters** to add **West Europe**, retain **Do not enforce**, then **Review + save > Save**. Update your resource-group location assignment and bonus initiative values below too, if you created them. Do not edit inherited or organizer-managed assignments. +If you already created your own exercise assignment using an earlier three- or four-region list, use **Edit assignment > Parameters** to include **West Europe** and **Australia East**, retain **Do not enforce**, then **Review + save > Save**. Update your resource-group location assignment and bonus initiative values below too, if you created them. Do not edit inherited or organizer-managed assignments. ### Step 3 - option B: Assign the Policy Using Azure CLI @@ -177,18 +178,18 @@ az policy assignment create \ --scope "/subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RESOURCE_GROUP" \ --policy "$POLICY_DEFINITION_ID" \ --enforcement-mode DoNotEnforce \ - --params '{"listOfAllowedLocations":{"value":["norwayeast","germanynorth","northeurope","westeurope"]}}' + --params '{"listOfAllowedLocations":{"value":["norwayeast","germanynorth","northeurope","westeurope","australiaeast"]}}' ``` ![image](./img/cloud-shell3.jpg) -Older screenshots may show only three regions; use all four locations in the current instructions. +Older screenshots may show fewer regions; use all five locations in the current instructions, including the lab-only exception. ### Step 4: Also Restrict Resource Group Locations ⚠️ **Important**: The "Allowed locations" policy applies to resources, but resource groups have their own location metadata. The separate **"Allowed locations for resource groups"** policy evaluates that metadata. At your resource-group scope, this is a compliance demonstration for your existing group; it does not restrict the creation of other resource groups. Subscription-wide enforcement belongs to the organizer, not participants. -**Using Azure Portal:** In **Policy > Assignments > Assign policy**, choose **Allowed locations for resource groups** (definition ID ending `e765b5de-1225-4ba3-bd56-1ac6695af988`). Set scope to your resource group, assignment name to `Lab User-0024 - Restrict Resource Groups to Sovereign Regions` (use your number), and enforcement to **Do not enforce**. Under **Parameters**, select Norway East, Germany North, North Europe, and West Europe, then **Review + create > Create**. +**Using Azure Portal:** In **Policy > Assignments > Assign policy**, choose **Allowed locations for resource groups** (definition ID ending `e765b5de-1225-4ba3-bd56-1ac6695af988`). Set scope to your resource group, assignment name to `Lab User-0024 - Restrict Resource Groups to Sovereign Regions` (use your number), and enforcement to **Do not enforce**. Under **Parameters**, select Norway East, Germany North, North Europe, West Europe, and Australia East (lab-only exception), then **Review + create > Create**. **Using Azure CLI:** @@ -202,7 +203,7 @@ az policy assignment create \ --policy "$RG_POLICY_DEFINITION_ID" \ --params '{ "listOfAllowedLocations": { - "value": ["norwayeast", "germanynorth", "northeurope", "westeurope"] + "value": ["norwayeast", "germanynorth", "northeurope", "westeurope", "australiaeast"] } }' \ --enforcement-mode DoNotEnforce @@ -406,8 +407,8 @@ az policy assignment create \ | Policy shown on the Policies tab | Values on the Policy parameters tab | |---|---| -| Allowed locations | Norway East, Germany North, North Europe, West Europe | -| Allowed locations for resource groups | Norway East, Germany North, North Europe, West Europe | +| Allowed locations | Norway East, Germany North, North Europe, West Europe, Australia East (lab-only exception) | +| Allowed locations for resource groups | Norway East, Germany North, North Europe, West Europe, Australia East (lab-only exception) | | Require a tag and its value on resources | Tag name: `DataClassification`; Tag value: `Sovereign` | | Not allowed resource types | `Microsoft.Network/publicIPAddresses` | @@ -425,7 +426,8 @@ az policy assignment create \ "norwayeast", "germanynorth", "northeurope", - "westeurope" + "westeurope", + "australiaeast" ] } } @@ -438,7 +440,8 @@ az policy assignment create \ "norwayeast", "germanynorth", "northeurope", - "westeurope" + "westeurope", + "australiaeast" ] } } diff --git a/03-Azure/01-03-Infrastructure/01_Sovereign_Cloud/walkthrough/challenge-06/solution-06.md b/03-Azure/01-03-Infrastructure/01_Sovereign_Cloud/walkthrough/challenge-06/solution-06.md index 2a315bafd..c55b2cc66 100644 --- a/03-Azure/01-03-Infrastructure/01_Sovereign_Cloud/walkthrough/challenge-06/solution-06.md +++ b/03-Azure/01-03-Infrastructure/01_Sovereign_Cloud/walkthrough/challenge-06/solution-06.md @@ -104,7 +104,7 @@ LocalBox runs as a nested lab environment hosted in Azure. In a production sover Verify that **Custom location** points to the shared LocalBox environment and **Virtual machine kind** is **Azure Local**. Leave **Storage path** set to **Choose automatically** unless the facilitator instructs otherwise. > [!NOTE] -> The selected custom location determines the Azure region used for the VM's management resources; it need not match your resource group's location or the Azure region hosting the LocalBox simulator. Fresh hosted deployments and the manual deployment default to **West Europe**. Earlier hosted test deployments used **Australia East**. Inspect the actual custom location in your event rather than assuming the latest default has changed an existing environment. +> The selected custom location determines the Azure region used for the VM's management resources; it need not match your resource group's location or the Azure region hosting the LocalBox simulator. Fresh hosted deployments and the manual deployment default to **Australia East**, including LocalBox's staging storage account, after a hosted test encountered a West Europe eligibility rejection. This is a lab-only choice, not a European data-residency recommendation. Inspect the actual custom location in your event rather than assuming the latest default has changed an existing environment. > > In production, the workload runs on the Azure Local hardware, while Azure stores management data in the registration region. In this lab, that hardware is simulated inside Azure-hosted LocalBox. Registration in Australia East does **not** mean that the nested VM's compute has moved there, but the management-data location still matters for sovereignty and Azure Policy. See [Azure Local regions](https://learn.microsoft.com/azure/azure-local/concepts/system-requirements-23h2#azure-requirements) and [Azure Local data handling](https://learn.microsoft.com/azure/azure-local/faq#does-my-data-stored-on-azure-local-get-sent-to-the-cloud). @@ -170,7 +170,7 @@ Do not change the resource group, select another team's custom location, or disa - **Different error code or no policy identifiers:** retain the exact error for the facilitator. A message mentioning a region is not by itself proof that the Challenge 1 policy caused the failure. 4. After the authorized correction has propagated, retry validation in your assigned resource group. Check for partially created resources before retrying a submitted deployment; do not delete shared LocalBox resources. -The current Challenge 1 exercise allowlist includes West Europe to match fresh LocalBox deployments; older assignments may still have only three regions. This does not permit earlier test deployments registered in Australia East. Do not broaden organizer-managed or inherited allowlists without the policy owner's approval. The updated registration-region default applies to fresh deployments, not existing custom locations. The hosted `SecurityControl=Ignore` and `CostControl=Ignore` tags are **not** general Azure Policy exemptions. +The current Challenge 1 exercise allowlist retains West Europe and includes **Australia East as a lab-only exception** for LocalBox management resources; older assignments may still have only three or four regions. Keep your exercise assignments in **DoNotEnforce** mode and update only your own assignments following Challenge 1. Do not broaden organizer-managed or inherited allowlists without the policy owner's approval. The updated registration-region default applies to fresh deployments, not existing custom locations. The hosted `SecurityControl=Ignore` and `CostControl=Ignore` tags are **not** general Azure Policy exemptions. Reference: [Resolve RequestDisallowedByPolicy errors](https://learn.microsoft.com/azure/azure-resource-manager/troubleshooting/error-policy-requestdisallowedbypolicy).