diff --git a/03-Azure/01-03-Infrastructure/01_Sovereign_Cloud/labautomation/deploy-lab.ps1 b/03-Azure/01-03-Infrastructure/01_Sovereign_Cloud/labautomation/deploy-lab.ps1 new file mode 100644 index 000000000..9d2825a9c --- /dev/null +++ b/03-Azure/01-03-Infrastructure/01_Sovereign_Cloud/labautomation/deploy-lab.ps1 @@ -0,0 +1,69 @@ +<# +.SYNOPSIS +Deploys the lab resources scoped to a subscription or resource group. +.DESCRIPTION +Provides a controlled deployment flow for lab environments, optionally limited to a resource group and specific Entra user IDs. +.PARAMETER DeploymentType +Defines the deployment scope; allowed values are subscription or resourcegroup. +.PARAMETER SubscriptionId +Specifies the Azure subscription that contains the lab resources. +.PARAMETER ResourceGroupName +In case of resourcegroup deployment, specifies the target resource group name. +.PARAMETER PreferredLocation +Specifies the preferred Azure regions (ordered by preference) for resource deployment. An empty array indicates no preference. +.PARAMETER AllowedEntraUserIds +Optional list of Entra user object IDs permitted to access the lab resources. +#> +param( + [Parameter(Mandatory=$true)] + [ValidateSet('subscription','resourcegroup', 'resourcegroup-with-subscriptionowner')] + [string]$DeploymentType, + + [Parameter(Mandatory=$true)] + [string]$SubscriptionId, + + [string]$ResourceGroupName = "", + + [string[]]$PreferredLocation = @(), + + [string[]]$AllowedEntraUserIds = @() +) + +# Validate parameters +if($DeploymentType -eq 'resourcegroup' -and [string]::IsNullOrEmpty($ResourceGroupName)) { + throw "ResourceGroupName must be provided when DeploymentType is 'resourcegroup'." +} + +# set the effective location (used as the metadata location for the subscription-scoped deployment) +if($PreferredLocation.Count -gt 0) { + $effectiveLocation = $PreferredLocation[0] +} else { + $effectiveLocation = "swedencentral" # Default location if no preference is provided +} + +# With deploymentType = resourcegroup the platform has already created one resource +# group per participant in the shared subscription and granted the participant Owner +# on it. Surface that resource group name on the participant's dashboard. +if(-not [string]::IsNullOrEmpty($ResourceGroupName)) { + @{"HackboxCredential" = @{ name = "Resource Group Name"; value = $ResourceGroupName; note = "Your dedicated resource group (you have Owner)" }} +} + +# Register the resource providers required by the Sovereign Cloud lab on the shared +# subscription (the platform has already set the Azure context to $SubscriptionId). +Write-Host "Registering required resource providers..." +& (Join-Path $PSScriptRoot 'resource-providers.ps1') + +# Assign the lab-specific subscription-scoped RBAC (Security Reader + Resource Policy +# Contributor) to the participant. +# main.bicep targets the subscription scope, so it is deployed with New-AzSubscriptionDeployment. +$deploymentName = "lab-" + (Get-MhhStableHash -Value $AllowedEntraUserIds -Length 24) + +Write-Host "Assigning subscription-scoped lab RBAC to participant $($AllowedEntraUserIds[0])..." +New-AzSubscriptionDeployment ` + -Name $deploymentName ` + -Location $effectiveLocation ` + -TemplateFile (Join-Path $PSScriptRoot 'main.bicep') ` + -TemplateParameterObject @{ + userObjectId = $AllowedEntraUserIds[0] + resourceGroupName = $ResourceGroupName + } | Out-Null \ No newline at end of file diff --git a/03-Azure/01-03-Infrastructure/01_Sovereign_Cloud/labautomation/lab-defaults.json b/03-Azure/01-03-Infrastructure/01_Sovereign_Cloud/labautomation/lab-defaults.json new file mode 100644 index 000000000..68e612b42 --- /dev/null +++ b/03-Azure/01-03-Infrastructure/01_Sovereign_Cloud/labautomation/lab-defaults.json @@ -0,0 +1,8 @@ +{ + "$schema": "https://raw.githubusercontent.com/microsoft/MicroHack/refs/heads/main/lab-defaults-schema.json", + "groups": [], + "deploymentType": "resourcegroup", + "labsPerSubscription": 60, + "preferredLocation": "swedencentral, italynorth, spaincentral", + "estimatedDailyCostsUsd": 5.0 +} diff --git a/03-Azure/01-03-Infrastructure/01_Sovereign_Cloud/labautomation/main.bicep b/03-Azure/01-03-Infrastructure/01_Sovereign_Cloud/labautomation/main.bicep new file mode 100644 index 000000000..4743633de --- /dev/null +++ b/03-Azure/01-03-Infrastructure/01_Sovereign_Cloud/labautomation/main.bicep @@ -0,0 +1,53 @@ +// Sovereign Cloud MicroHack - per-participant lab RBAC +// +// deploymentType = resourcegroup: the platform pre-creates one resource group +// per participant in the shared subscription and grants the participant Owner on +// that resource group plus Reader on the subscription. This template adds the +// same subscription-scoped permissions that resources/subscription-preparations/3-rbac.ps1 +// previously assigned to the LabUsers group: +// - Security Reader (view Defender for Cloud secure score / recommendations) +// - Resource Policy Contributor (author and assign Azure Policy at subscription scope) +// +// The role assignments are subscription-scoped, so this deployment must target the +// subscription (see deploy-lab.ps1, which invokes it with New-AzSubscriptionDeployment). + +targetScope = 'subscription' + +@description('Entra object ID of the lab participant to grant the lab-specific subscription-scoped RBAC roles to.') +param userObjectId string + +@description('Name of the participant resource group (created by the platform) to grant resource-group-scoped RBAC roles in.') +param resourceGroupName string + +// Built-in role definition IDs (stable across all Azure subscriptions) +var securityReaderRoleId = '39bc4728-0917-49c7-9d2c-d95423bc2eb4' +var resourcePolicyContributorRoleId = '36243c78-bf99-498c-9df9-86d9f8d28608' + +resource securityReaderAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(subscription().id, userObjectId, securityReaderRoleId) + properties: { + principalId: userObjectId + principalType: 'User' + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', securityReaderRoleId) + } +} + +resource resourcePolicyContributorAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(subscription().id, userObjectId, resourcePolicyContributorRoleId) + properties: { + principalId: userObjectId + principalType: 'User' + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', resourcePolicyContributorRoleId) + } +} + +// Resource-group-scoped roles for the participant's dedicated resource group +// (Owner is already granted by the platform). +module resourceGroupRbac 'rg-rbac.bicep' = { + name: 'lab-rg-rbac' + scope: resourceGroup(resourceGroupName) + params: { + userObjectId: userObjectId + } +} + diff --git a/03-Azure/01-03-Infrastructure/01_Sovereign_Cloud/labautomation/resource-providers.ps1 b/03-Azure/01-03-Infrastructure/01_Sovereign_Cloud/labautomation/resource-providers.ps1 new file mode 100644 index 000000000..4453a8099 --- /dev/null +++ b/03-Azure/01-03-Infrastructure/01_Sovereign_Cloud/labautomation/resource-providers.ps1 @@ -0,0 +1,151 @@ +<# +.SYNOPSIS + Register required Azure resource providers for the Sovereign Cloud MicroHack. + +.DESCRIPTION + This script registers all required Azure resource providers needed for the + Sovereign Cloud MicroHack across all available subscriptions. + + Required providers include: + - Azure Arc and hybrid connectivity + - Azure Local (Stack HCI) + - Confidential computing + - Key Vault and encryption + - Monitoring and policy + +.EXAMPLE + .\1-resource-providers.ps1 + Registers all required resource providers + +.NOTES + Author: MicroHack Team + Date: January 2026 + +.LINK + https://learn.microsoft.com/azure/azure-resource-manager/management/resource-providers-and-types +#> + +[CmdletBinding()] +param( +) + +# Ensure required modules are available +$requiredModules = @('Az.Accounts', 'Az.Resources') +foreach ($module in $requiredModules) { + if (-not (Get-Module -ListAvailable -Name $module)) { + Write-Error "$module module is not installed. Please run: Install-Module -Name $module" + exit 1 + } +} + +# Import required modules +Import-Module Az.Accounts, Az.Resources -ErrorAction Stop + +Write-Host "`n=== Resource Provider Registration Utility ===" -ForegroundColor Cyan +Write-Host "This script will register all required resource providers for the Sovereign Cloud MicroHack." +Write-Host "" + +# Check if user is logged in +try { + $context = Get-AzContext + if (-not $context) { + Write-Host "No Azure context found. Please login..." -ForegroundColor Yellow + $context = Get-AzContext + } else { + Write-Host "Using Azure account: $($context.Account.Id)" -ForegroundColor Green + } +} catch { + Write-Error "Failed to get Azure context. Please run Connect-AzAccount first." + exit 1 +} + +# Get all subscriptions +$subscriptions = Get-AzSubscription + +if ($subscriptions.Count -eq 0) { + Write-Error "No subscriptions found. Please ensure you have access to at least one subscription." + exit 1 +} + +Write-Host "Found $($subscriptions.Count) subscription(s)" -ForegroundColor Green + +# List of resource providers to register for Sovereign Cloud MicroHack +$providers = @( + # Azure Arc and Hybrid + "Microsoft.HybridCompute", + "Microsoft.GuestConfiguration", + "Microsoft.HybridConnectivity", + "Microsoft.AzureArcData", + + # Azure Local (Stack HCI) + "Microsoft.AzureStackHCI", + "Microsoft.ResourceConnector", + "Microsoft.HybridContainerService", + + # Compute and Confidential Computing + "Microsoft.Compute", + "Microsoft.ConfidentialLedger", + + # Security and Compliance + "Microsoft.Security", + "Microsoft.PolicyInsights", + "Microsoft.Advisor", + + # Monitoring and Operations + "Microsoft.OperationsManagement", + "Microsoft.OperationalInsights", + "Microsoft.Insights", + "Microsoft.Monitor", + + # Key Vault and Encryption + "Microsoft.KeyVault", + "Microsoft.ManagedIdentity", + + # Networking + "Microsoft.Network", + + # Storage + "Microsoft.Storage", + + # Attestation (for Confidential Computing) + "Microsoft.Attestation", + + # Kubernetes (for AKS Arc) + "Microsoft.Kubernetes", + "Microsoft.KubernetesConfiguration", + "Microsoft.ContainerService", + + # Extended Location (for Azure Local) + "Microsoft.ExtendedLocation" +) + +Write-Host "`nResource providers to register:" -ForegroundColor Yellow +foreach ($provider in $providers) { + Write-Host " - $provider" -ForegroundColor Gray +} + +# Register resource providers for current subscription + + foreach ($provider in $providers) { + $existingProvider = Get-AzResourceProvider -ProviderNamespace $provider -ErrorAction SilentlyContinue + + if ($existingProvider.RegistrationState -eq 'Registered') { + Write-Host " [REGISTERED] $provider" -ForegroundColor Green + } else { + Write-Host " [REGISTERING] $provider..." -ForegroundColor Yellow + try { + Register-AzResourceProvider -ProviderNamespace $provider | Out-Null + Write-Host " Registration initiated" -ForegroundColor Gray + } catch { + Write-Host " Failed to register: $($_.Exception.Message)" -ForegroundColor Red + } + } + } + + +Write-Host "`n" + ("=" * 80) -ForegroundColor Cyan +Write-Host "SUMMARY" -ForegroundColor Cyan +Write-Host ("=" * 80) -ForegroundColor Cyan +Write-Host "Resource provider registration initiated for subscription: $($SubscriptionId)" -ForegroundColor Green +Write-Host "`nNote: Some providers may take a few minutes to fully register." -ForegroundColor Yellow +Write-Host "You can check registration status with: Get-AzResourceProvider -ProviderNamespace " -ForegroundColor Gray diff --git a/03-Azure/01-03-Infrastructure/01_Sovereign_Cloud/labautomation/rg-rbac.bicep b/03-Azure/01-03-Infrastructure/01_Sovereign_Cloud/labautomation/rg-rbac.bicep new file mode 100644 index 000000000..8158bc431 --- /dev/null +++ b/03-Azure/01-03-Infrastructure/01_Sovereign_Cloud/labautomation/rg-rbac.bicep @@ -0,0 +1,35 @@ +// Sovereign Cloud MicroHack - per-participant resource-group-scoped RBAC +// +// Deployed as a module (scope = the participant's resource group) from main.bicep. +// Adds the resource-group-scoped roles (per participant). +// Owner on the resource group is already granted by the platform (deploymentType = +// resourcegroup), so only the two additional roles are assigned here: +// - Key Vault Administrator +// - Storage Account Contributor + +targetScope = 'resourceGroup' + +@description('Entra object ID of the lab participant to grant the resource-group-scoped RBAC roles to.') +param userObjectId string + +// Built-in role definition IDs (stable across all Azure subscriptions) +var keyVaultAdministratorRoleId = '00482a5a-887f-4fb3-b363-3b7fe8e74483' +var storageAccountContributorRoleId = '17d1049b-9a84-46fb-8f53-869881c3d3ab' + +resource keyVaultAdministratorAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(resourceGroup().id, userObjectId, keyVaultAdministratorRoleId) + properties: { + principalId: userObjectId + principalType: 'User' + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', keyVaultAdministratorRoleId) + } +} + +resource storageAccountContributorAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(resourceGroup().id, userObjectId, storageAccountContributorRoleId) + properties: { + principalId: userObjectId + principalType: 'User' + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', storageAccountContributorRoleId) + } +} diff --git a/99-MicroHack-Template/labautomation/main.bicep b/99-MicroHack-Template/labautomation/main.bicep new file mode 100644 index 000000000..e69de29bb