From 336a0395dadf64e4ae87b51c9f0226aa86187de6 Mon Sep 17 00:00:00 2001 From: Digvijay Chauhan Date: Sun, 1 Nov 2026 20:49:20 +0100 Subject: [PATCH] Regulatory Intelligence: register resource providers in shared-deploy-lab.ps1 - labautomation/shared-deploy-lab.ps1 registers the resource providers that infra/main.bicep deploys, once per subscription before the labs start, and waits for them. Participants own only their resource group and cannot register a provider. Best-effort: a provider that cannot be registered is reported with the fix command instead of stopping every lab. - dev/run-local.ps1 runs it first, as the platform does. README updated. - Tested with the platform helper module (adminpwsh image) and with dev/run-local.ps1. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../dev/run-local.ps1 | 8 ++- .../labautomation/README.md | 4 +- .../labautomation/shared-deploy-lab.ps1 | 63 +++++++++++++++++++ .../tests/test_content.py | 2 +- 4 files changed, 72 insertions(+), 5 deletions(-) create mode 100644 03-Azure/01-04-AI/11_Healthcare_Regulatory_Intelligence/labautomation/shared-deploy-lab.ps1 diff --git a/03-Azure/01-04-AI/11_Healthcare_Regulatory_Intelligence/dev/run-local.ps1 b/03-Azure/01-04-AI/11_Healthcare_Regulatory_Intelligence/dev/run-local.ps1 index 4e20172d3..4807be58a 100644 --- a/03-Azure/01-04-AI/11_Healthcare_Regulatory_Intelligence/dev/run-local.ps1 +++ b/03-Azure/01-04-AI/11_Healthcare_Regulatory_Intelligence/dev/run-local.ps1 @@ -1,6 +1,7 @@ # Local-only wrapper to run labautomation/deploy-lab.ps1 outside the MicroHack platform. -# Supplies the platform-provided helper (Get-MhhStableHash), pre-creates the resource -# group, and passes the signed-in user's Entra object ID, exactly as the platform would. +# Supplies the platform-provided helper (Get-MhhStableHash), runs +# labautomation/shared-deploy-lab.ps1, pre-creates the resource group, and passes the +# signed-in user's Entra object ID, exactly as the platform would. # Not part of the participant flow: for facilitators testing the lab automation. # # Prereqs: Connect-AzAccount (Az PowerShell) AND az login (Azure CLI) to the SAME subscription. @@ -80,6 +81,9 @@ if (-not (Get-Command bicep -ErrorAction SilentlyContinue)) { } } +# The platform runs the once-per-subscription hook before any lab. +& "$PSScriptRoot\..\labautomation\shared-deploy-lab.ps1" -SubscriptionId $SubscriptionId -PreferredLocation @($Location) -AllowedEntraUserIds $AllowedEntraUserIds + $started = Get-Date $outputs = & "$PSScriptRoot\..\labautomation\deploy-lab.ps1" @arguments Write-Host ("[OK] Deployed in {0:N0} minutes. Lab dashboard values:" -f ((Get-Date) - $started).TotalMinutes) diff --git a/03-Azure/01-04-AI/11_Healthcare_Regulatory_Intelligence/labautomation/README.md b/03-Azure/01-04-AI/11_Healthcare_Regulatory_Intelligence/labautomation/README.md index 597020afa..ae822888f 100644 --- a/03-Azure/01-04-AI/11_Healthcare_Regulatory_Intelligence/labautomation/README.md +++ b/03-Azure/01-04-AI/11_Healthcare_Regulatory_Intelligence/labautomation/README.md @@ -35,7 +35,7 @@ The platform sets the Az context and passes `DeploymentType`, `SubscriptionId`, Dashboard outputs (not secrets): `ResourceGroupName`, `EffectiveLocation`, `FoundryProjectEndpoint`, `FoundryAccountName`, `ModelDeploymentName`, `EmbeddingDeploymentName`, `StorageAccountName`, `SearchServiceName`, `SearchEndpoint`, `ApplicationInsightsName`. -Register these resource providers before the event: Microsoft.CognitiveServices, Microsoft.Storage, Microsoft.Search, Microsoft.OperationalInsights and Microsoft.Insights. +[`shared-deploy-lab.ps1`](shared-deploy-lab.ps1) registers these resource providers once per subscription, before the labs start, and waits for them: Microsoft.CognitiveServices, Microsoft.Storage, Microsoft.Search, Microsoft.OperationalInsights and Microsoft.Insights. Participants own only their resource group and cannot register a provider. If a provider cannot be registered, the script prints the command for a subscription owner and lets the labs continue. ## Capacity and cost @@ -48,7 +48,7 @@ Register these resource providers before the event: Microsoft.CognitiveServices, ## Testing the automation outside the platform -[`dev/run-local.ps1`](../dev/run-local.ps1) runs `deploy-lab.ps1` the way the platform does: it supplies `Get-MhhStableHash`, creates the resource group, passes your Entra object ID and prints the dashboard values. Sign in with both `Connect-AzAccount` and `az login` to the same subscription, then run `./dev/run-local.ps1` from the lab folder (add `-Location ` to check another region). It needs the Bicep CLI; if only the Azure CLI's copy is installed, the wrapper uses it. Delete the resource group afterwards. +[`dev/run-local.ps1`](../dev/run-local.ps1) runs `shared-deploy-lab.ps1` and `deploy-lab.ps1` the way the platform does: it supplies `Get-MhhStableHash`, creates the resource group, passes your Entra object ID and prints the dashboard values. Sign in with both `Connect-AzAccount` and `az login` to the same subscription, then run `./dev/run-local.ps1` from the lab folder (add `-Location ` to check another region). It needs the Bicep CLI; if only the Azure CLI's copy is installed, the wrapper uses it. Delete the resource group afterwards. ## Lifecycle diff --git a/03-Azure/01-04-AI/11_Healthcare_Regulatory_Intelligence/labautomation/shared-deploy-lab.ps1 b/03-Azure/01-04-AI/11_Healthcare_Regulatory_Intelligence/labautomation/shared-deploy-lab.ps1 new file mode 100644 index 000000000..72e49a5e8 --- /dev/null +++ b/03-Azure/01-04-AI/11_Healthcare_Regulatory_Intelligence/labautomation/shared-deploy-lab.ps1 @@ -0,0 +1,63 @@ +<# +.SYNOPSIS +Runs once per subscription, before any deploy-lab.ps1 run starts. +.DESCRIPTION +Registers the resource providers that infra/main.bicep deploys. The participant owns only their resource group and +cannot register a provider (a subscription-level action), so a missing registration would fail every lab. +Best-effort: if this script throws, the platform runs no deploy-lab.ps1 in the subscription, so a provider that +cannot be registered is reported with the command to fix it instead. +.PARAMETER SubscriptionId +Specifies the Azure subscription that contains the lab resources. +.PARAMETER PreferredLocation +Specifies the preferred Azure regions (ordered by preference) for resource deployment. +.PARAMETER AllowedEntraUserIds +Entra user object IDs of every participant holding a lab in this subscription. +#> +param( + [Parameter(Mandatory = $true)] + [string]$SubscriptionId, + + [Parameter(Mandatory = $true)] + [string[]]$PreferredLocation = @(), + + [Parameter(Mandatory = $false)] + [string[]]$AllowedEntraUserIds = @() +) + +$ErrorActionPreference = 'Stop' + +$providers = @( + 'Microsoft.CognitiveServices', + 'Microsoft.Storage', + 'Microsoft.Search', + 'Microsoft.OperationalInsights', + 'Microsoft.Insights' +) + +$pending = @() +foreach ($provider in $providers) { + try { + $state = (Get-AzResourceProvider -ProviderNamespace $provider -ErrorAction SilentlyContinue | Select-Object -First 1).RegistrationState + if ($state -eq 'Registered') { + Write-Host "[OK] [$SubscriptionId] $provider is registered." + continue + } + Write-Host "[INFO] [$SubscriptionId] Registering $provider (state: '$state')..." + Register-AzResourceProvider -ProviderNamespace $provider -ErrorAction Stop | Out-Null + $pending += $provider + } catch { + Write-Host "[WARN] [$SubscriptionId] Could not register $provider`: $_" + Write-Host "[WARN] [$SubscriptionId] A subscription owner can run: az provider register --namespace $provider --subscription $SubscriptionId" + } +} + +# Registration is asynchronous and the labs start as soon as this script returns, so wait for it. +for ($waited = 0; $pending.Count -gt 0 -and $waited -lt 300; $waited += 15) { + Start-Sleep -Seconds 15 + $pending = @($pending | Where-Object { + (Get-AzResourceProvider -ProviderNamespace $_ -ErrorAction SilentlyContinue | Select-Object -First 1).RegistrationState -ne 'Registered' + }) +} +foreach ($provider in $pending) { + Write-Host "[WARN] [$SubscriptionId] $provider was still registering after 5 minutes. The first labs may fail with MissingSubscriptionRegistration." +} \ No newline at end of file diff --git a/03-Azure/01-04-AI/11_Healthcare_Regulatory_Intelligence/tests/test_content.py b/03-Azure/01-04-AI/11_Healthcare_Regulatory_Intelligence/tests/test_content.py index 8b5fd41a7..49a14a59e 100644 --- a/03-Azure/01-04-AI/11_Healthcare_Regulatory_Intelligence/tests/test_content.py +++ b/03-Azure/01-04-AI/11_Healthcare_Regulatory_Intelligence/tests/test_content.py @@ -84,7 +84,7 @@ def test_no_em_dashes_anywhere(): def test_platform_automation_layout(): - assert {p.name for p in (LAB / "labautomation").iterdir()} == {"deploy-lab.ps1", "lab-defaults.json", "README.md"} + assert {p.name for p in (LAB / "labautomation").iterdir()} == {"deploy-lab.ps1", "shared-deploy-lab.ps1", "lab-defaults.json", "README.md"} defaults = json.loads((LAB / "labautomation" / "lab-defaults.json").read_text(encoding="utf-8")) assert defaults["deploymentType"] in {"resourcegroup", "subscription", "resourcegroup-with-subscriptionowner"} assert defaults["$schema"].endswith("/lab-defaults-schema.json")