From 79ac3f99e2ce0ce600f6069605145b2ff3beca9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Schumacher?= Date: Fri, 28 Aug 2026 16:01:09 +0200 Subject: [PATCH] Fix deployment issues --- .../labautomation/deploy-lab.ps1 | 22 ++-- .../infra/modules/sqlManagedInstance.bicep | 21 +++ .../labautomation/infra/modules/vnet.bicep | 22 +++- .../labautomation/main.bicep | 4 +- .../labautomation/shared-deploy-lab.ps1 | 122 ++++++++++-------- .../labautomation/shared.bicep | 16 +++ 6 files changed, 134 insertions(+), 73 deletions(-) diff --git a/03-Azure/01-02 Data/03-Talk_to_your_data/labautomation/deploy-lab.ps1 b/03-Azure/01-02 Data/03-Talk_to_your_data/labautomation/deploy-lab.ps1 index ee547bbb8..5b47ec5bd 100644 --- a/03-Azure/01-02 Data/03-Talk_to_your_data/labautomation/deploy-lab.ps1 +++ b/03-Azure/01-02 Data/03-Talk_to_your_data/labautomation/deploy-lab.ps1 @@ -33,9 +33,12 @@ Set-StrictMode -Version Latest $ErrorActionPreference = 'Stop' $SharedResourceGroup = 'rg-shared' +$SqlAdminLogin = 'sqlmiadmin' $FabricApi = 'https://api.fabric.microsoft.com/v1' $TailspinToysBak = 'tailspintoys_before_launch.bak' $TailspinToysFeedbackBak = 'tailspintoysfeedback_before_launch.bak' +# Shared MI admin password: derive at subscription scope so it matches the shared hook's value. +$sqlPassword = New-MhhStablePassword -Purpose 'sql-admin' -Length 24 -ResourceGroupName '' # ───────────────────────────────────────────── # Helpers @@ -45,10 +48,9 @@ if (-not (Get-Module -ListAvailable -Name SqlServer)) { } Import-Module SqlServer -ErrorAction Stop -function Get-DbAccessToken { - $t = (Get-AzAccessToken -ResourceUrl 'https://database.windows.net/').Token - if ($t -is [System.Security.SecureString]) { return (ConvertFrom-SecureString $t -AsPlainText) } - return $t +function Update-MhhTokenQuiet { + # Refresh Azure credentials; Update-MhhToken's status object is shown only with -Verbose. + Update-MhhToken | Out-String | Write-Verbose } function Invoke-MiSql { @@ -58,7 +60,9 @@ function Invoke-MiSql { [string]$Query, [int]$QueryTimeout = 0 ) - Invoke-Sqlcmd -ServerInstance $Server -Database $Database -AccessToken (Get-DbAccessToken) ` + # SQL authentication with the MI admin login: the platform cannot set an Entra admin on the MI. + $cred = [pscredential]::new($SqlAdminLogin, (ConvertTo-SecureString $sqlPassword -AsPlainText -Force)) + Invoke-Sqlcmd -ServerInstance $Server -Database $Database -Credential $cred ` -Query $Query -ConnectionTimeout 30 -QueryTimeout $QueryTimeout -ErrorAction Stop } @@ -102,7 +106,7 @@ $server = "$publicFqdn,3342" # ───────────────────────────────────────────── # 1. Per-attendee ARM resources (CSV storage) into the attendee's RG # ───────────────────────────────────────────── -Update-MhhToken +Update-MhhTokenQuiet $rgResult = Invoke-MhhDeploymentWithRegionFallback ` -PreferredLocations $PreferredLocation ` -ResourceGroupName $ResourceGroupName ` @@ -125,7 +129,7 @@ if ($LASTEXITCODE -ne 0) { throw "Failed to upload employee CSV." } # ───────────────────────────────────────────── # 2. Restore the two attendee databases in the shared SQL MI # ───────────────────────────────────────────── -Update-MhhToken +Update-MhhTokenQuiet $storageAccount = az storage account list -g $SharedResourceGroup --query "[0].name" -o tsv $containerName = 'build' $storageKey = az storage account keys list --resource-group $SharedResourceGroup --account-name $storageAccount --query "[0].value" -o tsv @@ -161,7 +165,7 @@ foreach ($r in $restores) { # ───────────────────────────────────────────── # 3. Attendee login/user (db_owner) + product in each database # ───────────────────────────────────────────── -Update-MhhToken +Update-MhhTokenQuiet Invoke-MiSql -Server $server -Database 'master' -QueryTimeout 60 -Query @" IF NOT EXISTS (SELECT 1 FROM sys.server_principals WHERE name = N'$upn') CREATE LOGIN [$upn] FROM EXTERNAL PROVIDER; @@ -189,7 +193,7 @@ if ($productExists -ne 1) { # ───────────────────────────────────────────── # 4. Fabric workspace on the shared capacity + attendee as Member # ───────────────────────────────────────────── -Update-MhhToken +Update-MhhTokenQuiet $capacityName = az resource list -g $SharedResourceGroup --resource-type 'Microsoft.Fabric/capacities' --query "[0].name" -o tsv $capacity = (Invoke-FabricApi -Method GET -Path 'capacities').value | Where-Object { $_.displayName -eq $capacityName } | Select-Object -First 1 if (-not $capacity) { throw "Fabric capacity '$capacityName' not visible via the Fabric API." } diff --git a/03-Azure/01-02 Data/03-Talk_to_your_data/labautomation/infra/modules/sqlManagedInstance.bicep b/03-Azure/01-02 Data/03-Talk_to_your_data/labautomation/infra/modules/sqlManagedInstance.bicep index 8109a0e6a..90220bf3b 100644 --- a/03-Azure/01-02 Data/03-Talk_to_your_data/labautomation/infra/modules/sqlManagedInstance.bicep +++ b/03-Azure/01-02 Data/03-Talk_to_your_data/labautomation/infra/modules/sqlManagedInstance.bicep @@ -23,6 +23,15 @@ param administratorLogin string @secure() param administratorLoginPassword string +@description('Entra admin display name (label only).') +param aadAdminLogin string + +@description('Entra admin object ID (sid). Set explicitly so ARM does not resolve the principal.') +param aadAdminSid string + +@description('Entra admin tenant ID.') +param aadAdminTenantId string + @description('Tags to apply to all resources.') param tags object = {} @@ -55,6 +64,18 @@ resource managedInstance 'Microsoft.Sql/managedInstances@2023-08-01-preview' = { } } +// Entra admin enables Azure AD authentication on the instance (SQL auth stays enabled). Explicit sid so ARM does not have to resolve the principal. +resource sqlAadAdmin 'Microsoft.Sql/managedInstances/administrators@2023-08-01-preview' = { + parent: managedInstance + name: 'ActiveDirectory' + properties: { + administratorType: 'ActiveDirectory' + login: aadAdminLogin + sid: aadAdminSid + tenantId: aadAdminTenantId + } +} + output id string = managedInstance.id output name string = managedInstance.name output fqdn string = managedInstance.properties.fullyQualifiedDomainName diff --git a/03-Azure/01-02 Data/03-Talk_to_your_data/labautomation/infra/modules/vnet.bicep b/03-Azure/01-02 Data/03-Talk_to_your_data/labautomation/infra/modules/vnet.bicep index ccccc2340..0b073d613 100644 --- a/03-Azure/01-02 Data/03-Talk_to_your_data/labautomation/infra/modules/vnet.bicep +++ b/03-Azure/01-02 Data/03-Talk_to_your_data/labautomation/infra/modules/vnet.bicep @@ -18,6 +18,9 @@ param addressSpace array = [ @description('Tags to apply to all resources.') param tags object = {} +@description('When true, the SQL MI subnet NSG and route table already exist (the MI has injected network intent policies). They are then referenced instead of redeployed, which avoids ConflictWithNetworkIntentPolicy on shared hook re-runs.') +param sqlMiNetworkingExists bool = false + var mergedTags = union(tags, { environment: envName }) @@ -28,7 +31,7 @@ var mergedTags = union(tags, { // ManagedInstance subnet NSG. In the Terraform version the NSG was created empty // and the 3342 rule was added by the SQL MI module; here it is inlined. -resource sqlMiNsg 'Microsoft.Network/networkSecurityGroups@2023-11-01' = { +resource sqlMiNsg 'Microsoft.Network/networkSecurityGroups@2023-11-01' = if (!sqlMiNetworkingExists) { name: 'nsg-sqlhackmi' location: location tags: mergedTags @@ -51,6 +54,11 @@ resource sqlMiNsg 'Microsoft.Network/networkSecurityGroups@2023-11-01' = { } } +// Referenced (not redeployed) when the SQL MI has already injected its network intent policies. +resource sqlMiNsgExisting 'Microsoft.Network/networkSecurityGroups@2023-11-01' existing = { + name: 'nsg-sqlhackmi' +} + resource bastionNsg 'Microsoft.Network/networkSecurityGroups@2023-11-01' = { name: 'sqlhack-shared-bastion-nsg' location: location @@ -415,7 +423,7 @@ resource streamingNsg 'Microsoft.Network/networkSecurityGroups@2023-11-01' = { // ───────────────────────────────────────────── // Route table for the ManagedInstance subnet // ───────────────────────────────────────────── -resource sqlMiRouteTable 'Microsoft.Network/routeTables@2023-11-01' = { +resource sqlMiRouteTable 'Microsoft.Network/routeTables@2023-11-01' = if (!sqlMiNetworkingExists) { name: 'rt-sqlhackmi' location: location tags: mergedTags @@ -424,6 +432,10 @@ resource sqlMiRouteTable 'Microsoft.Network/routeTables@2023-11-01' = { } } +resource sqlMiRouteTableExisting 'Microsoft.Network/routeTables@2023-11-01' existing = { + name: 'rt-sqlhackmi' +} + // ───────────────────────────────────────────── // Virtual network with inline subnets // ───────────────────────────────────────────── @@ -453,10 +465,10 @@ resource vnet 'Microsoft.Network/virtualNetworks@2023-11-01' = { ] defaultOutboundAccess: false networkSecurityGroup: { - id: sqlMiNsg.id + id: sqlMiNetworkingExists ? sqlMiNsgExisting.id : sqlMiNsg.id } routeTable: { - id: sqlMiRouteTable.id + id: sqlMiNetworkingExists ? sqlMiRouteTableExisting.id : sqlMiRouteTable.id } delegations: [ { @@ -565,4 +577,4 @@ output vnetId string = vnet.id output managedInstanceSubnetId string = resourceId('Microsoft.Network/virtualNetworks/subnets', vnet.name, 'ManagedInstance') output appServiceSubnetId string = resourceId('Microsoft.Network/virtualNetworks/subnets', vnet.name, 'snet-appservice') output fabricSubnetName string = 'fabric_vnet' -output sqlMiNsgName string = sqlMiNsg.name +output sqlMiNsgName string = 'nsg-sqlhackmi' diff --git a/03-Azure/01-02 Data/03-Talk_to_your_data/labautomation/main.bicep b/03-Azure/01-02 Data/03-Talk_to_your_data/labautomation/main.bicep index 0ca4d27bb..0172905f5 100644 --- a/03-Azure/01-02 Data/03-Talk_to_your_data/labautomation/main.bicep +++ b/03-Azure/01-02 Data/03-Talk_to_your_data/labautomation/main.bicep @@ -3,8 +3,8 @@ // resources and are created by deploy-lab.ps1 against the shared MI/capacity. // The employee CSV blob is uploaded by deploy-lab.ps1 (not an ARM operation). -@description('Azure region.') -param location string +@description('Azure region. Defaults to the resource group location so it follows the region-fallback helper.') +param location string = resourceGroup().location @description('Entra object ID of the attendee, granted data access to the CSV storage.') param attendeeObjectId string diff --git a/03-Azure/01-02 Data/03-Talk_to_your_data/labautomation/shared-deploy-lab.ps1 b/03-Azure/01-02 Data/03-Talk_to_your_data/labautomation/shared-deploy-lab.ps1 index 6c5bd3401..6e806e3c8 100644 --- a/03-Azure/01-02 Data/03-Talk_to_your_data/labautomation/shared-deploy-lab.ps1 +++ b/03-Azure/01-02 Data/03-Talk_to_your_data/labautomation/shared-deploy-lab.ps1 @@ -32,8 +32,8 @@ $ErrorActionPreference = 'Stop' $SharedResourceGroup = 'rg-shared' $SqlAdminLogin = 'sqlmiadmin' $FabricApi = 'https://api.fabric.microsoft.com/v1' -$GraphApi = 'https://graph.microsoft.com/v1.0' -$RequiredProviders = @('Microsoft.Sql', 'Microsoft.Fabric', 'Microsoft.Storage', 'Microsoft.Web', 'Microsoft.Network') +# Microsoft.PowerPlatform is required for the Fabric VNet data gateway's subnet delegation. +$RequiredProviders = @('Microsoft.Sql', 'Microsoft.Fabric', 'Microsoft.Storage', 'Microsoft.Web', 'Microsoft.Network', 'Microsoft.PowerPlatform') # Demo databases restored once for the whole subscription: DB name -> backup file. $TailspinToysBak = 'tailspintoys_before_launch.bak' @@ -53,10 +53,9 @@ if (-not (Get-Module -ListAvailable -Name SqlServer)) { } Import-Module SqlServer -ErrorAction Stop -function Get-DbAccessToken { - $t = (Get-AzAccessToken -ResourceUrl 'https://database.windows.net/').Token - if ($t -is [System.Security.SecureString]) { return (ConvertFrom-SecureString $t -AsPlainText) } - return $t +function Update-MhhTokenQuiet { + # Refresh Azure credentials; Update-MhhToken's status object is shown only with -Verbose. + Update-MhhToken | Out-String | Write-Verbose } function Invoke-MiSql { @@ -67,10 +66,12 @@ function Invoke-MiSql { [string]$InputFile, [int]$QueryTimeout = 0 ) + # SQL authentication with the MI admin login: the platform cannot set an Entra admin on the MI. + $cred = [pscredential]::new($SqlAdminLogin, (ConvertTo-SecureString $sqlPassword -AsPlainText -Force)) $splat = @{ ServerInstance = $Server Database = $Database - AccessToken = (Get-DbAccessToken) + Credential = $cred ConnectionTimeout = 30 QueryTimeout = $QueryTimeout ErrorAction = 'Stop' @@ -99,33 +100,6 @@ function Invoke-FabricApi { return ($raw | ConvertFrom-Json) } -function Ensure-DirectoryReadersForIdentity { - param([Parameter(Mandatory = $true)][string]$PrincipalId) - - # Directory Readers well-known template id. - $templateId = '88d8e3e3-8f55-4a1e-953a-9b9898b8876b' - # Activate the directory role if it is only present as a template. - $roles = (az rest --method GET --url "$GraphApi/directoryRoles" --resource 'https://graph.microsoft.com' 2>$null | ConvertFrom-Json).value - $role = $roles | Where-Object { $_.roleTemplateId -eq $templateId } | Select-Object -First 1 - if (-not $role) { - $activated = az rest --method POST --url "$GraphApi/directoryRoles" --resource 'https://graph.microsoft.com' ` - --headers 'Content-Type=application/json' --body (@{ roleTemplateId = $templateId } | ConvertTo-Json -Compress) 2>&1 | ConvertFrom-Json - $role = $activated - } - # Is the identity already a member? - $members = (az rest --method GET --url "$GraphApi/directoryRoles/$($role.id)/members" --resource 'https://graph.microsoft.com' 2>$null | ConvertFrom-Json).value - if ($members | Where-Object { $_.id -eq $PrincipalId }) { - Write-Host "[shared] SQL MI identity already has Directory Readers." - return - } - $ref = @{ '@odata.id' = "$GraphApi/directoryObjects/$PrincipalId" } | ConvertTo-Json -Compress - az rest --method POST --url "$GraphApi/directoryRoles/$($role.id)/members/`$ref" --resource 'https://graph.microsoft.com' ` - --headers 'Content-Type=application/json' --body $ref 2>&1 | Out-Null - if ($LASTEXITCODE -ne 0) { throw "Failed to add SQL MI identity to Directory Readers." } - Write-Host "[shared] Granted Directory Readers to SQL MI identity. Waiting for propagation." - Start-Sleep -Seconds 60 -} - # ───────────────────────────────────────────── # 0. Location + resource providers # ───────────────────────────────────────────── @@ -155,18 +129,48 @@ foreach ($uid in ($AllowedEntraUserIds | Where-Object { $_ })) { # ───────────────────────────────────────────── # 2. Resolve the deploying service principal (becomes SQL MI Entra admin) # ───────────────────────────────────────────── -$spObjectId = (Get-AzADServicePrincipal -ApplicationId (Get-AzContext).Account.Id -ErrorAction SilentlyContinue).Id +$account = (Get-AzContext).Account.Id +$spObjectId = $null +if ($account -as [guid]) { + $spObjectId = (Get-AzADServicePrincipal -ApplicationId $account -ErrorAction SilentlyContinue).Id + if (-not $spObjectId) { + $spObjectId = az ad sp show --id $account --query id -o tsv 2>$null + } +} if (-not $spObjectId) { - $spObjectId = az ad sp show --id (Get-AzContext).Account.Id --query id -o tsv 2>$null + # Local testing runs as a user, not an SP; fall back to the signed-in user. + $spObjectId = (Get-AzADUser -SignedIn -ErrorAction SilentlyContinue).Id } -if (-not $spObjectId) { throw "Could not resolve the deploying service principal object id." } +if (-not $spObjectId) { throw "Could not resolve the deploying principal object id." } -$fabricAdminMembers = @($spObjectId) + ($AllowedEntraUserIds | Where-Object { $_ }) | Select-Object -Unique +# Entra admin for the SQL MI (enables Entra auth; set declaratively with an explicit sid so ARM does not resolve the principal). +$aadTenantId = (Get-AzContext).Tenant.Id +$aadAdminLogin = if ($account -as [guid]) { (Get-AzADServicePrincipal -Id $spObjectId -ErrorAction SilentlyContinue).DisplayName } else { $account } +if (-not $aadAdminLogin) { $aadAdminLogin = 'MicroHackDeployer' } + +# Fabric capacity admin members: users must be UPNs (object IDs are rejected); a service principal uses its object ID. +$fabricMemberList = [System.Collections.Generic.List[string]]::new() +if ($account -as [guid]) { + $fabricMemberList.Add($spObjectId) # deploying service principal +} +else { + $fabricMemberList.Add($account) # deploying user's UPN +} +foreach ($uid in ($AllowedEntraUserIds | Where-Object { $_ })) { + $memberUpn = (Get-MhhLabUser -UserId $uid -ErrorAction SilentlyContinue).UserPrincipalName + if (-not $memberUpn) { $memberUpn = (Get-AzADUser -ObjectId $uid -ErrorAction SilentlyContinue).UserPrincipalName } + if ($memberUpn) { $fabricMemberList.Add($memberUpn) } + else { Write-Warning "[shared] Could not resolve a UPN for '$uid'; omitting it from Fabric admin members." } +} +$fabricAdminMembers = @($fabricMemberList | Select-Object -Unique) $sqlPassword = New-MhhStablePassword -Purpose 'sql-admin' -Length 24 # ───────────────────────────────────────────── # 3. Deploy the shared ARM stack (async; SQL MI can exceed the 90-min command limit) # ───────────────────────────────────────────── +# Once the SQL MI exists it has injected network intent policies into its subnet's NSG/route table; +# redeploying those conflicts, so tell the template to reference them as existing on re-runs. +$sqlMiNetworkingExists = -not [string]::IsNullOrWhiteSpace((az sql mi list -g $SharedResourceGroup --query "[0].id" -o tsv 2>$null)) $depName = "shared-$(Get-Date -f yyyyMMddHHmmss)" Write-Host "[shared] Submitting shared.bicep deployment '$depName' into '$SharedResourceGroup'." New-AzResourceGroupDeployment ` @@ -174,17 +178,24 @@ New-AzResourceGroupDeployment ` -ResourceGroupName $SharedResourceGroup ` -TemplateFile (Join-Path $PSScriptRoot 'shared.bicep') ` -TemplateParameterObject @{ - location = $location - sqlAdminLogin = $SqlAdminLogin - sqlPassword = (ConvertTo-SecureString $sqlPassword -AsPlainText -Force) - fabricAdminMembers = $fabricAdminMembers + location = $location + sqlAdminLogin = $SqlAdminLogin + # Plain string, not SecureString: -AsJob cannot serialize a SecureString across the job boundary. Bicep param stays @secure(). + sqlPassword = $sqlPassword + fabricAdminMembers = $fabricAdminMembers + sqlMiNetworkingExists = $sqlMiNetworkingExists + sqlAadAdminLogin = $aadAdminLogin + sqlAadAdminSid = $spObjectId + sqlAadAdminTenantId = $aadTenantId } ` -AsJob | Out-Null do { Start-Sleep -Seconds 30 - Update-MhhToken - $state = (Get-AzResourceGroupDeployment -ResourceGroupName $SharedResourceGroup -Name $depName -ErrorAction SilentlyContinue).ProvisioningState + Update-MhhTokenQuiet + # Guard the property access: under Set-StrictMode the deployment may not be registered yet (returns $null). + $dep = Get-AzResourceGroupDeployment -ResourceGroupName $SharedResourceGroup -Name $depName -ErrorAction SilentlyContinue + $state = if ($dep) { $dep.ProvisioningState } else { $null } Write-Host "[shared] deployment state: $state" } while ($state -notin 'Succeeded', 'Failed', 'Canceled') @@ -195,7 +206,6 @@ if ($state -ne 'Succeeded') { $out = (Get-AzResourceGroupDeployment -ResourceGroupName $SharedResourceGroup -Name $depName).Outputs $miName = $out.sqlManagedInstanceName.Value $miFqdn = $out.sqlManagedInstanceFqdn.Value -$miIdentity = $out.sqlManagedInstanceIdentityPrincipalId.Value $capacityName = $out.fabricCapacityName.Value $vnetName = $out.vnetName.Value $fabricSubnet = $out.fabricSubnetName.Value @@ -208,23 +218,21 @@ $publicFqdn = $miFqdn -replace '^([^.]+)\.', '$1.public.' $server = "$publicFqdn,3342" # ───────────────────────────────────────────── -# 4. Entra: Directory Readers for the MI identity + MI Entra admin (deploying SP) +# 4. Entra: Directory Readers for the MI identity (needed for external-provider logins) # ───────────────────────────────────────────── -Update-MhhToken -Ensure-DirectoryReadersForIdentity -PrincipalId $miIdentity - -$existingAdmin = az sql mi ad-admin list --resource-group $SharedResourceGroup --managed-instance $miName --query "[0].sid" -o tsv 2>$null -if (-not $existingAdmin) { - Write-Host "[shared] Setting SQL MI Entra admin to the deploying principal." - az sql mi ad-admin create --resource-group $SharedResourceGroup --managed-instance $miName ` - --display-name 'MicroHackDeployer' --object-id $spObjectId 2>&1 | Out-Null - if ($LASTEXITCODE -ne 0) { throw "Failed to set SQL MI Entra admin." } +Update-MhhTokenQuiet +# Grant the SQL MI managed identity Directory Readers via the platform helper (handles the tenant-wide directory-role lock and is idempotent). +$drResult = @(Get-AzSqlInstance -ResourceGroupName $SharedResourceGroup -Name $miName | + Set-MhhManagedIdentityRoleMember -Role 'Directory Readers') +if ($drResult.status -contains 'Failed') { + throw "Failed to grant Directory Readers to the SQL MI identity." } +Write-Host "[shared] SQL MI identity Directory Readers: $($drResult.status -join ', ')." # ───────────────────────────────────────────── # 5. Upload the .bak files and restore the demo databases # ───────────────────────────────────────────── -Update-MhhToken +Update-MhhTokenQuiet $storageKey = az storage account keys list --resource-group $SharedResourceGroup --account-name $storageAccount --query "[0].value" -o tsv if ($LASTEXITCODE -ne 0) { throw "Failed to read storage key for '$storageAccount'." } @@ -286,7 +294,7 @@ if ($jobExists -ne 1) { # ───────────────────────────────────────────── # 7. Fabric VNet data gateway + ConnectionCreator for every attendee # ───────────────────────────────────────────── -Update-MhhToken +Update-MhhTokenQuiet $capacity = (Invoke-FabricApi -Method GET -Path 'capacities').value | Where-Object { $_.displayName -eq $capacityName } | Select-Object -First 1 if (-not $capacity) { throw "Fabric capacity '$capacityName' not visible via the Fabric API (check tenant setting 'Service principals can use Fabric APIs')." } diff --git a/03-Azure/01-02 Data/03-Talk_to_your_data/labautomation/shared.bicep b/03-Azure/01-02 Data/03-Talk_to_your_data/labautomation/shared.bicep index d0003601f..84d543dcb 100644 --- a/03-Azure/01-02 Data/03-Talk_to_your_data/labautomation/shared.bicep +++ b/03-Azure/01-02 Data/03-Talk_to_your_data/labautomation/shared.bicep @@ -22,6 +22,15 @@ param sqlAdminLogin string @secure() param sqlPassword string +@description('Entra admin display name for the SQL MI (label only).') +param sqlAadAdminLogin string + +@description('Entra admin object ID (sid) for the SQL MI.') +param sqlAadAdminSid string + +@description('Entra admin tenant ID for the SQL MI.') +param sqlAadAdminTenantId string + @description('Fabric capacity SKU name.') param fabricSkuName string = 'F32' @@ -31,6 +40,9 @@ param fabricAdminMembers array @description('Base database the shared webshop connects to.') param webshopSqlDatabase string = 'TailspinToys_Demo_Final' +@description('When true, the SQL MI subnet NSG and route table already exist and are referenced instead of redeployed (avoids ConflictWithNetworkIntentPolicy on shared hook re-runs).') +param sqlMiNetworkingExists bool = false + var commonTags = { SecurityControl: 'Ignore' } @@ -41,6 +53,7 @@ module vnet 'infra/modules/vnet.bicep' = { location: location envName: envName tags: commonTags + sqlMiNetworkingExists: sqlMiNetworkingExists } } @@ -53,6 +66,9 @@ module sqlManagedInstance 'infra/modules/sqlManagedInstance.bicep' = { subnetId: vnet.outputs.managedInstanceSubnetId administratorLogin: sqlAdminLogin administratorLoginPassword: sqlPassword + aadAdminLogin: sqlAadAdminLogin + aadAdminSid: sqlAadAdminSid + aadAdminTenantId: sqlAadAdminTenantId tags: commonTags } }