Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions 03-Azure/01-03-Infrastructure/01_Sovereign_Cloud/Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,15 +65,15 @@ Use **GitHub Codespaces with the Azure / Infra / Sovereign Cloud devcontainer**

![Codespace creation options showing the Sovereign Cloud devcontainer and a 2-core machine](./img/codespaces-create.png)

4. In **Terminal > New Terminal**, sign in to Azure using the tenant and subscription shown in the Hackathon Console:
4. In **Terminal > New Terminal**, sign in to Azure with your workshop account and select the subscription shown in the Hackathon Console. You do not need to enter a tenant ID; the workshop account belongs to a single tenant.

```bash
az login --use-device-code --tenant "<Tenant ID>"
az login --use-device-code
az account set --subscription "<Subscription ID>"
az account show --query "{Account:user.name,Subscription:name,Tenant:tenantId}" --output table
```

Open the device-login URL printed by Azure CLI and enter its **device code**. Sign in with your **hacker account**, using the **Temporary Access Pass (TAP)** from the Console when prompted. The TAP and device code are different; never put either in files or commands. Use a private browser window if necessary to avoid signing in with your normal work account. For bring-your-own-subscription labs, use your own Azure identity instead.
Open the device-login URL printed by Azure CLI and enter its **device code**. Sign in with your **hacker account**, using the **Temporary Access Pass (TAP)** from the Console when prompted. The TAP and device code are different; never put either in files or commands. Use a private browser window if necessary to avoid signing in with your normal work account. Confirm that the displayed account and subscription match your workshop details before continuing. For bring-your-own-subscription labs, use your own Azure identity instead.
5. Use this same Codespace for all challenges. The Explorer opens at the Sovereign Cloud folder, with the repository already cloned. Use **Bash** for Challenges 1-3 and 7; for Challenges 4-5, open a terminal and run `pwsh` to enter **PowerShell 7**. Challenge 6 is primarily portal-based.

### Terminals, breaks and saved work
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,46 +2,44 @@

## Goal

The goal of this challenge is to operate a sovereign hybrid cloud environment by combining Microsoft Sovereign Public Cloud and Sovereign Private Cloud components. You will work with Azure Local, simulated via Azure Arc Jumpstart LocalBox, and provision your own VM. You will use Azure Arc to manage that VM through Azure, review its security posture with Microsoft Defender for Cloud, and assess its OS updates with Azure Update Manager. You will also deploy a container application in your team's namespace on the shared AKS cluster and access it privately from your Sovereign Cloud Codespace.
Explore how Azure Arc manages VMs and Kubernetes workloads on Azure Local, simulated by LocalBox. Create and manage a VM, then review the AKS load balancer, deploy a sample application, and connect to it using Arc Proxy.

## Scenario

Your organization must run workloads in a sovereign cloud while still leveraging Azure's management and governance capabilities. Azure Local represents your sovereign on-premises infrastructure, and Azure Arc enables you to apply consistent governance across your hybrid estate.

## Actions

Before deploying, confirm that your Challenge 1 exercise policies are back in
**DoNotEnforce** and ask the facilitator to confirm that the shared LocalBox custom
location's Azure region is permitted in your assigned resource group. This region
can differ from the resource group's location. If a policy blocks creation, follow
the [location-policy troubleshooting steps](../walkthrough/challenge-06/solution-06.md#if-validation-or-deployment-is-blocked-by-a-location-policy);
do not disable organizer-managed policies or change allowlists yourself.
Before starting, return your Challenge 1 exercise policies to **DoNotEnforce**. If a policy blocks deployment, use the [troubleshooting steps](https://github.com/microsoft/MicroHack/blob/main/03-Azure/01-03-Infrastructure/01_Sovereign_Cloud/walkthrough/challenge-06/solution-06.md#if-validation-or-deployment-is-blocked-by-a-location-policy) or ask the facilitator.

### Explore and manage a VM

* Explore the LocalBox hybrid infrastructure in the Azure Portal
* Deploy your own VM on Azure Local using Azure Arc VM management and verify that guest management is connected
* Verify Microsoft Defender for Cloud coverage and review security recommendations for the VM you provisioned
* Use Azure Update Manager to assess OS updates on the VM you provisioned
* Verify your Console-provided Microsoft Entra administrator-group membership and the existing Azure Arc Enabled Kubernetes Cluster User Role with the facilitator; portal workload visibility alone does not prove group membership
* Inspect the organizer-prepared `arcnetworking` extension (`microsoft.arcnetworking`) on `localbox-aks` in `rg-localbox-shared`: confirm **Succeeded**, healthy MetalLB workloads, and the existing **`aks-pool`** with **ARP** advertisement and organizer-reserved service VIPs. Do not install or configure shared networking
* Follow [Task 5 of the walkthrough](../walkthrough/challenge-06/solution-06.md#task-5-deploy-a-container-to-the-aks-cluster-deployed-on-azure-local) in **Bash**: run `az connectedk8s proxy` with Microsoft Entra authentication and an isolated kubeconfig, derive a unique namespace from your assigned resource group, and deploy `aks-local-sample-app.yaml` only in that namespace
* Verify successful rollout and the Service's assigned IP, then keep the proxy and namespace-scoped `kubectl port-forward service/aks-container-1 8080:80` running in separate terminals. Open port **8080** through the **Private** Codespaces Ports view (or `localhost:8080` when running directly on a local workstation). Never expose the API proxy port or use service-account/admin tokens; no RDP or static routes are needed
* Clean up only your team's application resources and, when no longer needed by teammates, your exercise namespace. Leave shared infrastructure unchanged

The organizer prepares MetalLB with `resources/prepare-localbox.ps1`; the Console's LocalBox deployment hook alone is not sufficient. If extension health, the pool, or access prerequisites are missing, contact the facilitator rather than installing extensions, creating pools, or granting additional permissions.
### Review the AKS load balancer and deploy an app

* Review the preinstalled **MetalLB load balancer**: check that it is healthy and understand how it assigns IP addresses to applications
* Deploy the sample application to your team's namespace on the shared AKS cluster and inspect its assigned service IP
* Connect using **Azure Arc Proxy** and port forwarding, then open the application privately in your browser
* Clean up your team's application resources, leaving the shared cluster and load balancer unchanged

Follow [Task 5 of the walkthrough](https://github.com/microsoft/MicroHack/blob/main/03-Azure/01-03-Infrastructure/01_Sovereign_Cloud/walkthrough/challenge-06/solution-06.md#task-5-deploy-a-container-to-the-aks-cluster-deployed-on-azure-local) for the commands and checks. The infrastructure is already prepared; you do not need to install MetalLB or access the LocalBox Client VM. Ask the facilitator if access or health checks fail.

The default service VIP range is **`10.10.0.10-10.10.0.100`**, excluding nodes **`10.10.0.101-10.10.0.199`**, control-plane IP **`10.10.0.5`**, and gateway **`10.10.0.1`**. Confirm any organizer customization. Inspect the pool's **IPAddressPool** and **L2Advertisement** in `kube-system` read-only; participants do not create them.
In a real deployment, clients would reach the app through its MetalLB IP address. In this lab, Arc Proxy and port forwarding provide private access without requiring a direct route to the cluster.

## Success criteria

* You can navigate and understand the LocalBox hybrid environment in the Azure Portal
* You have deployed your own VM on Azure Local via the Azure Portal and verified that guest management is Enabled (Connected)
* You have verified Defender for Servers coverage for your VM and reviewed its available recommendations, or identified that its assessment is still pending
* You have completed an Azure Update Manager assessment for your VM and reviewed the results, including when no updates are pending
* You have verified the shared MetalLB extension, workloads, and reserved ARP IP pool without changing shared infrastructure
* You have deployed the sample application to your team's unique AKS namespace, verified rollout, and recorded a Service IP from the reserved pool
* You have opened the application through a Microsoft Entra Arc proxy and a private application port-forward, with the API proxy never exposed
* You understand that production clients use the MetalLB VIP through configured network routing; proxy plus port-forward access does **not** validate that load-balancer network path
* You have cleaned up only your team's application resources
* You can explain MetalLB's role and have checked that the preinstalled load balancer is healthy
* Your sample application runs in your team's namespace and has a load-balancer IP address
* You can open the application privately using Arc Proxy and port forwarding, and explain how this differs from direct load-balancer access
* You have cleaned up your team's application resources without changing shared infrastructure
* You understand how Azure Arc provides a unified control plane for sovereign hybrid scenarios

## Learning resources
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,14 @@ Reference: [MetalLB on AKS on Azure Local](https://learn.microsoft.com/azure/aks

## Preparation execution details

If an older script stops at MetalLB discovery with `Invalid ARM collection response`,
the response may be valid: PowerShell background jobs deserialize JSON arrays as
`ArrayList`, which the earlier array-only check rejected. The corrected script
accepts both list representations while still rejecting malformed responses.
Use the corrected script and rerun with the same parameters after inspecting
resource status; do not delete the AKS cluster, storage, or networks to fix this
parsing error. Matching resources are reused.

Azure Local may append generated suffixes to its `UserStorage1` and `UserStorage2` resource names. The script resolves the actual storage-container ID and verifies its custom location; ambiguous names fail rather than selecting the first match.

On Windows, the script invokes Azure CLI through its bundled Python executable rather than `az.cmd`. This preserves arguments such as `ConvergedSwitch(compute_management)` that the batch wrapper otherwise interprets as command syntax. Keep the standard Azure CLI installation layout intact.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,9 @@ function Get-LocalBoxArmCollection {
param([Parameter(Mandatory)][string]$Url)
do {
$page = Invoke-LocalBoxAz @('rest', '--method', 'get', '--url', $Url)
if ($null -eq $page -or -not $page.Contains('value') -or $page.value -isnot [array]) {
# Background-job deserialization turns nested JSON arrays into ArrayList.
if ($page -isnot [System.Collections.IDictionary] -or -not $page.Contains('value') -or
$page.value -isnot [System.Collections.IList]) {
throw "Invalid ARM collection response for $Url; existing resources cannot be determined."
}
$page.value
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1459,6 +1459,47 @@ Describe 'CLI output streams' {
@{ Executable = $script:cliTestExecutable; Prefix = @('-NoProfile', '-NonInteractive', '-Command', ($script:cliTestCommand + "`n#")) }
}
}
It 'reads an ARM collection through the real job transport with <Count> entries' -ForEach @(
@{ Json = '{"value":[],"nextLink":null}'; Count = 0 }
@{ Json = '{"value":[{"id":"first"}]}'; Count = 1 }
@{ Json = '{"value":[{"id":"first"},{"id":"second"}]}'; Count = 2 }
) {
$script:cliTestCommand = "[Console]::Out.WriteLine('$Json'); exit 0"
$result = @(Get-LocalBoxArmCollection 'https://management.azure.com/test')
$result.Count | Should -Be $Count
if ($Count -ge 1) { $result[0].id | Should -Be 'first' }
if ($Count -eq 2) { $result[1].id | Should -Be 'second' }
}
It 'rejects malformed ARM collections through the real job transport: <Json>' -ForEach @(
@{ Json = '{}' }
@{ Json = '{"value":null}' }
@{ Json = '{"value":"not-a-list"}' }
@{ Json = '{"value":{"id":"not-a-list"}}' }
@{ Json = '"not-an-object"' }
) {
$script:cliTestCommand = "[Console]::Out.WriteLine('$Json'); exit 0"
{ Get-LocalBoxArmCollection 'https://management.azure.com/test' } | Should -Throw '*Invalid ARM collection response*'
}
It 'follows ARM pagination through the real job transport without losing entries' {
$script:cliTestCommand = @'
if ($args -contains 'https://management.azure.com/next') {
[Console]::Out.WriteLine('{"value":[{"id":"second"}],"nextLink":null}')
}
else {
[Console]::Out.WriteLine('{"value":[{"id":"first"}],"nextLink":"https://management.azure.com/next"}')
}
exit 0
'@
Mock Get-LocalBoxAzInvocation {
@{ Executable = $script:cliTestExecutable; Prefix = @('-NoProfile', '-NonInteractive', '-File', $script:collectionCliPath) }
}
$script:collectionCliPath = Join-Path $TestDrive 'collection-cli.ps1'
$script:cliTestCommand | Set-Content $script:collectionCliPath
$result = @(Get-LocalBoxArmCollection 'https://management.azure.com/first')
$result.Count | Should -Be 2
$result[0].id | Should -Be 'first'
$result[1].id | Should -Be 'second'
}
It 'parses JSON stdout when a successful native command also writes progress to stderr' {
$script:cliTestCommand = {
[Console]::Error.WriteLine('Progress: completing operation')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,13 @@ A fresh Codespace smoke test is still required to validate the devcontainer buil
## MetalLB preparation checks

MetalLB preparation tests are included in `prepare-localbox.tests.ps1`. Run
`Invoke-Pester ./prepare-localbox.tests.ps1 -FullName 'LocalBox MetalLB preparation*','MetalLB health evidence*' -Output Detailed`
`Invoke-Pester ./prepare-localbox.tests.ps1 -FullName 'LocalBox MetalLB preparation*','MetalLB health evidence*','CLI output streams*' -Output Detailed`
for the focused offline checks. They cover reserved VIP validation, idempotent
extension/pool installation, WhatIf, discovery failures, conflicting configuration
and failure propagation without deleting shared resources. Mocked health checks
and failure propagation without deleting shared resources. CLI tests use real
background-job serialization to cover empty, single-entry, multi-entry, paginated,
and malformed ARM collection responses, including deserialized `ArrayList` values.
Mocked health checks
also reject missing or mismatched address pools and L2 advertisements. The live LocalBox
suite now requires the `MetalLb` manifest fields; rerun preparation for older
manifests. Full checks also inspect the Kubernetes IPAddressPool and L2Advertisement.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,14 @@ Set up the variables that will be used throughout this challenge:
> [!IMPORTANT]
> The Azure CLI commands in this walkthrough use **Bash** syntax, not PowerShell. Bash is the default terminal in the Sovereign Cloud Codespace; no installation is needed there.

If you have not already signed in to Azure CLI with your workshop account, run:

```bash
az login --use-device-code
```

Open the displayed sign-in URL, enter the device code, and sign in with your **hacker account**, using the **Temporary Access Pass (TAP)** from the Console when prompted. No tenant ID is required for the workshop account. Skip this step if already signed in with the correct account, including in Azure Cloud Shell.

```bash
# Set common variables
# Customize RESOURCE_GROUP for each participant
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,14 @@ Please ensure that you successfully verified the [General prerequisites](https:/
> [!IMPORTANT]
> Use a **Bash terminal in your [Sovereign Cloud Codespace](https://github.com/microsoft/MicroHack/blob/main/03-Azure/01-03-Infrastructure/01_Sovereign_Cloud/Readme.md#recommended-environment-github-codespaces)**. These commands use Bash syntax, not PowerShell. Azure Cloud Shell (Bash) or a local Bash terminal with Azure CLI is an alternative for this challenge.

If you have not already signed in to Azure CLI with your workshop account, run:

```bash
az login --use-device-code
```

Open the displayed sign-in URL, enter the device code, and sign in with your **hacker account**, using the **Temporary Access Pass (TAP)** from the Console when prompted. No tenant ID is required for the workshop account. Skip this step if already signed in with the correct account, including in Azure Cloud Shell.

Set up the common variables that will be used throughout this challenge:

```bash
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
# Visual Attestation Demo v2 on Azure Container Instances

A self-contained ACI port of the AKS confidential-node attestation web UI from
`aks-samples/azure-voting-app/attestation/`. This is the **v2** of the original
[`visual-attestation-demo`](../visual-attestation-demo/) - same goal, simpler
footprint, and adds a one-shot `-Compare` mode that deploys both Confidential
`aks-samples/azure-voting-app/attestation/`. This **v2** sample has a simpler
footprint than the original demo and adds a one-shot `-Compare` mode that deploys both Confidential
and Standard SKUs side-by-side.

It demonstrates **runtime guest attestation** of an AMD SEV-SNP TEE via
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -291,9 +291,14 @@ Unblock-File ./Deploy-VisualAttestationV2.ps1
Use the same variable names as the other challenges. If these variables are
already present in your PowerShell session, do not generate a new suffix.

Use the **exact resource-group name assigned in the Console**, including the
`rg-` prefix (for example, `rg-labuser-0024`). If `RESOURCE_GROUP` is already
set, check that it matches; correct it before continuing. Only the attendee ID
omits the `rg-` prefix.

```powershell
if (-not $env:RESOURCE_GROUP) { $env:RESOURCE_GROUP = "labuser-xx" } # Your assigned group
if (-not $env:ATTENDEE_ID) { $env:ATTENDEE_ID = $env:RESOURCE_GROUP }
if (-not $env:RESOURCE_GROUP) { $env:RESOURCE_GROUP = "rg-labuser-0024" } # Replace with your exact assigned resource-group name
if (-not $env:ATTENDEE_ID) { $env:ATTENDEE_ID = $env:RESOURCE_GROUP -replace '^rg-', '' }
$env:LOCATION = "northeurope"

if (-not $env:HASH_SUFFIX) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ SEV-SNP confidential computing node pool** (2 nodes, `Standard_DC2as_v5`) and de
multi-container [Azure Voting App](https://github.com/Azure-Samples/azure-voting-app-redis) sample
to it, exposed via a public LoadBalancer.

The script follows the same conventions as [`vm-samples/BuildRandomCVM.ps1`](../../vm-samples/BuildRandomCVM.ps1):
random 5-letter suffix on the basename, full resource-group tagging (owner, BuiltBy, GitRepo,
The script uses a random 5-letter suffix on the basename, full resource-group tagging (owner, BuiltBy, GitRepo,
description, smoketest), CC SKU + AMD CVM vCPU quota preflight, and an optional `-smoketest` flag
that auto-deletes everything once the front-end is verified.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,11 @@ Otherwise, navigate to `03-Azure/01-03-Infrastructure/01_Sovereign_Cloud/walkthr

Set the **Sovereign Lab AKS Cluster** name from Console's Credentials tab. For
manual delivery, use the shared lab template's `aksClusterName` output.
Use your **exact assigned resource-group name**, including the `rg-` prefix
(for example, `rg-labuser-0024`), not just your attendee ID.

```powershell
$env:RESOURCE_GROUP = "labuser-xx"
$env:RESOURCE_GROUP = "rg-labuser-0024" # Replace with your exact assigned resource-group name
$env:AKS_CLUSTER = "<Sovereign Lab AKS Cluster>"
az aks show --resource-group $env:RESOURCE_GROUP --name $env:AKS_CLUSTER --query '{name:name,location:location,state:provisioningState}' --output table
```
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading