From fa7704d1f0fffff0db4e152f2deb7ae9745362d5 Mon Sep 17 00:00:00 2001 From: Bionic711 Date: Mon, 2 Mar 2026 16:48:44 -0600 Subject: [PATCH 1/8] fixes for MAG deployments --- deployers/azure.yaml | 267 +++++++++++++++---- deployers/bicep/cosmosDb-postDeployPerms.ps1 | 57 ++++ deployers/bicep/cosmosDb-postDeployPerms.sh | 3 +- deployers/bicep/main.bicep | 21 +- deployers/bicep/modules/cosmosDb.bicep | 6 +- deployers/bicep/modules/setPermissions.bicep | 2 +- deployers/bicep/modules/videoIndexer.bicep | 3 +- deployers/bicep/postconfig.py | 21 +- 8 files changed, 317 insertions(+), 63 deletions(-) create mode 100644 deployers/bicep/cosmosDb-postDeployPerms.ps1 diff --git a/deployers/azure.yaml b/deployers/azure.yaml index 1ce818e5..0081ce4f 100644 --- a/deployers/azure.yaml +++ b/deployers/azure.yaml @@ -13,9 +13,9 @@ services: context: ../../ dockerfile: application/single_app/Dockerfile hooks: - postprovision: # this is run after the infrastructure has been provisioned but before services are deployed. # primary use is to configure application settings and permissions + postprovision: posix: shell: sh run: | @@ -94,13 +94,79 @@ hooks: echo "========================================" else echo "" - echo "ℹ Skipping post-deployment configuration (var_configureApplication is not true)" + echo "i Skipping post-deployment configuration (var_configureApplication is not true)" echo "" echo "========================================" echo "POST-PROVISION: Completed (skipped)" echo "========================================" fi + windows: + shell: pwsh + run: | + $ErrorActionPreference = "Stop" + + Write-Host "========================================" + Write-Host "POST-PROVISION: Starting configuration" + Write-Host "========================================" + + if ($Env:var_configureApplication -eq "true") { + Write-Host "" + Write-Host "[1/4] Granting permissions to CosmosDB..." + if (pwsh -File ./bicep/cosmosDb-postDeployPerms.ps1) { + Write-Host "✓ CosmosDB permissions granted successfully" + } + else { + Write-Error "✗ ERROR: Failed to grant CosmosDB permissions" + exit 1 + } + + Write-Host "" + Write-Host "[2/4] Installing Python dependencies..." + python3 -m pip install --user -r ./bicep/requirements.txt *> $null + if ($LASTEXITCODE -eq 0) { + Write-Host "✓ Dependencies installed successfully" + } + else { + Write-Error "✗ ERROR: Failed to install Python dependencies" + exit 1 + } + + Write-Host "" + Write-Host "[3/4] Running post-deployment configuration..." + if (python3 ./bicep/postconfig.py) { + Write-Host "✓ Post-deployment configuration completed" + } + else { + Write-Error "✗ ERROR: Post-deployment configuration failed" + exit 1 + } + + Write-Host "" + Write-Host "[4/4] Restarting web service to apply settings..." + az webapp restart --name $Env:var_webService --resource-group $Env:var_rgName | Out-Null + if ($LASTEXITCODE -eq 0) { + Write-Host "✓ Web service restarted successfully" + } + else { + Write-Error "✗ ERROR: Failed to restart web service" + exit 1 + } + + Write-Host "" + Write-Host "========================================" + Write-Host "POST-PROVISION: Completed successfully" + Write-Host "========================================" + } + else { + Write-Host "" + Write-Host "i Skipping post-deployment configuration (var_configureApplication is not true)" + Write-Host "" + Write-Host "========================================" + Write-Host "POST-PROVISION: Completed (skipped)" + Write-Host "========================================" + } + predeploy: # this is run after infrastructure and postprovisioning but before service deployment # primary use is to build and push container images @@ -139,51 +205,25 @@ hooks: fi echo "" - echo "[2/6] Building Docker image..." - echo "Context: $(pwd)" - echo "Dockerfile: application/single_app/Dockerfile" - if docker build -f application/single_app/Dockerfile \ - -t ${var_containerRegistry}/${var_imageName}:${timestamp} . ; then - echo "✓ Docker image built successfully" - else - cleanup_on_error "Docker build" - fi - - echo "" - echo "[3/6] Tagging image as latest..." - if docker tag ${var_containerRegistry}/${var_imageName}:${timestamp} \ - ${var_containerRegistry}/${var_imageName}:latest ; then - echo "✓ Image tagged successfully" - else - cleanup_on_error "Docker tag" - fi - - echo "" - echo "[4/6] Logging in to ACR (${var_acrName})..." - if az acr login --name ${var_acrName}; then - echo "✓ ACR login successful" - else - cleanup_on_error "ACR login" - fi - - echo "" - echo "[5/6] Pushing images to ACR..." - echo " → Pushing latest tag..." - if docker push ${var_containerRegistry}/${var_imageName}:latest; then - echo " ✓ Latest tag pushed successfully" - else - cleanup_on_error "Docker push (latest)" - fi - - echo " → Pushing timestamped tag..." - if docker push ${var_containerRegistry}/${var_imageName}:${timestamp}; then - echo " ✓ Timestamped tag pushed successfully" + echo "[2/6] Building image in ACR..." + timestamp="$(date +"%Y%m%d-%H%M%S")" + image_ts="${var_imageName}:${timestamp}" + image_latest="${var_imageName}:latest" + + # Trigger remote build; context is repo root one level up from deployer + if az acr build \ + --registry "${var_acrName}" \ + --image "${image_ts}" \ + --image "${image_latest}" \ + --file application/single_app/Dockerfile \ + ./ ; then + echo "✓ ACR build completed and images pushed" else - cleanup_on_error "Docker push (timestamp)" + cleanup_on_error "ACR build" fi echo "" - echo "[6/6] Restarting web service..." + echo "[3/6] Restarting web service..." if az webapp start --name ${var_webService} --resource-group ${var_rgName}; then echo "✓ Web service restarted successfully" else @@ -195,6 +235,73 @@ hooks: echo "========================================" echo "PRE-DEPLOY: Completed successfully" echo "========================================" + + windows: + shell: pwsh + run: | + $ErrorActionPreference = "Stop" + + # Set up variables + Write-Host "$Env:var_acrName" + Write-Host "$Env:var_webService" + + function Cleanup-OnError([string]$step) { + Write-Error "✗ ERROR: Deployment failed at step: $step" + Write-Host "Attempting to restart web service..." + az webapp start --name $Env:var_webService --resource-group $Env:var_rgName 2>$null | Out-Null + exit 1 + } + + Write-Host "========================================" + Write-Host "PRE-DEPLOY: Building and pushing image" + Write-Host "========================================" + + Set-Location .. + $timestamp = Get-Date -Format "yyyyMMdd-HHmmss" + Write-Host "" + Write-Host "Deployment timestamp: $timestamp" + Write-Host "Image: $($Env:var_containerRegistry)/$($Env:var_imageName):$timestamp" + Write-Host "Current directory: $(Get-Location)" + + Write-Host "" + Write-Host "[1/6] Stopping web service..." + az webapp stop --name $Env:var_webService --resource-group $Env:var_rgName | Out-Null + if ($LASTEXITCODE -eq 0) { + Write-Host "✓ Web service stopped successfully" + } + else { + Write-Error "✗ ERROR: Failed to stop web service" + exit 1 + } + + Write-Host "" + Write-Host "[2/6] Building image in ACR..." + $timestamp = Get-Date -Format "yyyyMMdd-HHmmss" + $imageTs = "$($Env:var_imageName):$timestamp" + $imageLatest = "$($Env:var_imageName):latest" + + if (az acr build --registry $Env:var_acrName --image $imageTs --image $imageLatest --file application/single_app/Dockerfile ./) { + Write-Host "✓ ACR build completed and images pushed" + } + else { + Cleanup-OnError "ACR build" + } + + Write-Host "" + Write-Host "[3/6] Restarting web service..." + az webapp start --name $Env:var_webService --resource-group $Env:var_rgName | Out-Null + if ($LASTEXITCODE -eq 0) { + Write-Host "✓ Web service restarted successfully" + } + else { + Write-Error "✗ ERROR: Failed to restart web service" + exit 1 + } + + Write-Host "" + Write-Host "========================================" + Write-Host "PRE-DEPLOY: Completed successfully" + Write-Host "========================================" postup: # this is the final step to run after everything else is done @@ -261,10 +368,80 @@ hooks: echo "✓ Private networking configured successfully" else echo "" - echo "ℹ Skipping private networking configuration (var_enablePrivateNetworking is not true)" + echo "i: Skipping private networking configuration (var_enablePrivateNetworking is not true)" fi echo "" echo "========================================" echo "✓ DEPLOYMENT COMPLETED SUCCESSFULLY" - echo "========================================" \ No newline at end of file + echo "========================================" + + windows: + shell: pwsh + run: | + $ErrorActionPreference = "Stop" + + Write-Host "========================================" + Write-Host "POST-UP: Final configuration" + Write-Host "========================================" + + if ($Env:var_enablePrivateNetworking -eq "true") { + Write-Host "" + Write-Host "Configuring private networking..." + + Write-Host "" + Write-Host "[1/4] Disabling public network access for CosmosDB..." + az cosmosdb update --name $Env:var_cosmosDb_accountName --resource-group $Env:var_rgName --public-network-access Disabled | Out-Null + if ($LASTEXITCODE -eq 0) { + Write-Host "✓ CosmosDB public access disabled" + } + else { + Write-Error "✗ ERROR: Failed to disable CosmosDB public access" + exit 1 + } + + Write-Host "" + Write-Host "[2/4] Disabling public network access for Key Vault..." + az keyvault update --name $Env:var_keyVaultName --resource-group $Env:var_rgName --public-network-access Disabled | Out-Null + if ($LASTEXITCODE -eq 0) { + Write-Host "✓ Key Vault public access disabled" + } + else { + Write-Error "✗ ERROR: Failed to disable Key Vault public access" + exit 1 + } + + Write-Host "" + Write-Host "[3/4] Disabling public network access for Azure Container Registry..." + az acr update --name $Env:var_acrName --resource-group $Env:var_rgName --public-network-enabled false | Out-Null + if ($LASTEXITCODE -eq 0) { + Write-Host "✓ ACR public access disabled" + } + else { + Write-Error "✗ ERROR: Failed to disable ACR public access" + exit 1 + } + + Write-Host "" + Write-Host "[4/4] Disabling public network access for Web Application..." + az resource update --name $Env:var_webService --resource-group $Env:var_rgName --resource-type "Microsoft.Web/sites" --set properties.publicNetworkAccess=Disabled | Out-Null + if ($LASTEXITCODE -eq 0) { + Write-Host "✓ Web Application public access disabled" + } + else { + Write-Error "✗ ERROR: Failed to disable Web Application public access" + exit 1 + } + + Write-Host "" + Write-Host "✓ Private networking configured successfully" + } + else { + Write-Host "" + Write-Host "ℹ Skipping private networking configuration (var_enablePrivateNetworking is not true)" + } + + Write-Host "" + Write-Host "========================================" + Write-Host "✓ DEPLOYMENT COMPLETED SUCCESSFULLY" + Write-Host "========================================" \ No newline at end of file diff --git a/deployers/bicep/cosmosDb-postDeployPerms.ps1 b/deployers/bicep/cosmosDb-postDeployPerms.ps1 new file mode 100644 index 00000000..5e6f87d5 --- /dev/null +++ b/deployers/bicep/cosmosDb-postDeployPerms.ps1 @@ -0,0 +1,57 @@ +#!/usr/bin/env pwsh +$ErrorActionPreference = "Stop" + +# Expected environment variables (populated by azure.yaml): +# - var_rgName +# - var_cosmosDb_uri + +if (-not $Env:var_rgName) { throw "var_rgName is required" } +if (-not $Env:var_cosmosDb_uri) { throw "var_cosmosDb_uri is required" } + +$rgName = $Env:var_rgName +$cosmosUri = $Env:var_cosmosDb_uri + +# Extract account name from https://.documents.azure.com... +$uri = [Uri]$cosmosUri +# Works for commercial and gov (e.g., .azure.com, .azure.us); strip suffix after account name +$accountName = $uri.Host -replace '\.documents\.azure\..*','' +if ([string]::IsNullOrWhiteSpace($accountName)) { throw "Unable to parse Cosmos DB account name from URI: $cosmosUri" } + +Write-Host "===============================" +Write-Host "Cosmos DB Account Name: $accountName" + +$upn = az account show --query user.name -o tsv +$objectId = az ad signed-in-user show --query id -o tsv +$subscriptionId = az account show --query id -o tsv + +# Control-plane assignment +$scope = "/subscriptions/$subscriptionId/resourceGroups/$rgName/providers/Microsoft.DocumentDB/databaseAccounts/$accountName" +$roleName = "Contributor" +$roleId = az role definition list --name $roleName --query "[0].id" -o tsv + +Write-Host "Assigning role '$roleName' to user '$upn' on scope '$scope'..." +az role assignment create ` + --assignee-object-id $objectId ` + --assignee-principal-type User ` + --role $roleId ` + --scope $scope #` + #| Out-Null + +# Data-plane assignment +$dpRoleName = "Cosmos DB Built-in Data Contributor" +$dpRoleId = az cosmosdb sql role definition list ` + --account-name $accountName ` + --resource-group $rgName ` + --query "[?roleName=='$dpRoleName'].id | [0]" -o tsv + +Write-Host "Assigning data-plane role '$dpRoleName' to user '$upn' on Cosmos DB account '$accountName'..." +az cosmosdb sql role assignment create ` + --account-name $accountName ` + --resource-group $rgName ` + --scope "/" ` + --principal-id $objectId ` + --role-definition-id $dpRoleId #` + #| Out-Null + +Write-Host "Assigned Cosmos roles to $upn ($objectId)." +Write-Host "===============================" diff --git a/deployers/bicep/cosmosDb-postDeployPerms.sh b/deployers/bicep/cosmosDb-postDeployPerms.sh index c4d3c311..4cf28f22 100644 --- a/deployers/bicep/cosmosDb-postDeployPerms.sh +++ b/deployers/bicep/cosmosDb-postDeployPerms.sh @@ -5,7 +5,8 @@ set -euo pipefail RG_NAME="${var_rgName}" COSMOS_URI="${var_cosmosDb_uri}" -ACCOUNT_NAME=$(echo "$COSMOS_URI" | sed -E 's#https://([^.]*)\.documents\.azure\.com.*#\1#') +# Extract account name from commercial or sovereign (e.g., .azure.com, .azure.us), with optional port/path +ACCOUNT_NAME=$(echo "$COSMOS_URI" | sed -E 's#https://([^./]+)\.documents\.azure\.[^/:]+(:[0-9]+)?(/.*)?#$\1#') echo "===============================" echo "Cosmos DB Account Name: $ACCOUNT_NAME" diff --git a/deployers/bicep/main.bicep b/deployers/bicep/main.bicep index bcf19d31..d39e8c52 100644 --- a/deployers/bicep/main.bicep +++ b/deployers/bicep/main.bicep @@ -85,14 +85,14 @@ param gptModels array = [ { modelName: 'gpt-4.1' modelVersion: '2025-04-14' - skuName: 'GlobalStandard' - skuCapacity: 150 + skuName: cloudEnvironment == 'AzureCloud' ? 'GlobalStandard' : 'Standard' + skuCapacity: 50 } { modelName: 'gpt-4o' modelVersion: '2024-11-20' - skuName: 'GlobalStandard' - skuCapacity: 100 + skuName: cloudEnvironment == 'AzureCloud' ? 'GlobalStandard' : 'Standard' + skuCapacity: 50 } ] @@ -112,6 +112,17 @@ param embeddingModels array = [ } ] +@description('''Array of embedding model names to deploy to the OpenAI resource.''') +param embeddingModelsGov array = [ + { + modelName: 'text-embedding-ada-002' + modelVersion: '2' + skuName: cloudEnvironment == 'AzureCloud' ? 'GlobalStandard' : 'Standard' + skuCapacity: 50 + } + +] + //---------------- // allowed IP addresses for resources @description('''Comma separated list of IP addresses or ranges to allow access to resources when private networking is enabled. @@ -386,7 +397,7 @@ module openAI 'modules/openAI.bicep' = { configureApplicationPermissions: configureApplicationPermissions gptModels: gptModels - embeddingModels: embeddingModels + embeddingModels: cloudEnvironment == 'AzureCloud' ? embeddingModels : embeddingModelsGov enablePrivateNetworking: enablePrivateNetworking } diff --git a/deployers/bicep/modules/cosmosDb.bicep b/deployers/bicep/modules/cosmosDb.bicep index abb6f674..3249e544 100644 --- a/deployers/bicep/modules/cosmosDb.bicep +++ b/deployers/bicep/modules/cosmosDb.bicep @@ -28,11 +28,7 @@ resource cosmosDb 'Microsoft.DocumentDB/databaseAccounts@2023-04-15' = { properties: { publicNetworkAccess: 'Enabled' // configuration is set in post provision step in azure.yaml with post deployment script databaseAccountOfferType: 'Standard' - capabilities: [ - { - name: 'EnableServerless' - } - ] + capabilities: [] isVirtualNetworkFilterEnabled: enablePrivateNetworking ? true : false ipRules: enablePrivateNetworking ? allowedIpAddresses : [] diff --git a/deployers/bicep/modules/setPermissions.bicep b/deployers/bicep/modules/setPermissions.bicep index 675c12b9..bd164f16 100644 --- a/deployers/bicep/modules/setPermissions.bicep +++ b/deployers/bicep/modules/setPermissions.bicep @@ -59,7 +59,7 @@ resource contentSafety 'Microsoft.CognitiveServices/accounts@2025-06-01' existin name: contentSafetyName } -resource videoIndexerService 'Microsoft.VideoIndexer/accounts@2025-04-01' existing = if (videoIndexerName != '') { +resource videoIndexerService 'Microsoft.VideoIndexer/accounts@2025-03-01' existing = if (videoIndexerName != '') { name: videoIndexerName } diff --git a/deployers/bicep/modules/videoIndexer.bicep b/deployers/bicep/modules/videoIndexer.bicep index 786d7108..29a3a2bd 100644 --- a/deployers/bicep/modules/videoIndexer.bicep +++ b/deployers/bicep/modules/videoIndexer.bicep @@ -27,7 +27,7 @@ resource openAiService 'Microsoft.CognitiveServices/accounts@2024-10-01' existin } // deploy video indexer service if required -resource videoIndexerService 'Microsoft.VideoIndexer/accounts@2025-04-01' = { +resource videoIndexerService 'Microsoft.VideoIndexer/accounts@2025-03-01' = { name: toLower('${appName}-${environment}-video') location: location @@ -35,7 +35,6 @@ resource videoIndexerService 'Microsoft.VideoIndexer/accounts@2025-04-01' = { type: 'SystemAssigned' } properties: { - publicNetworkAccess: enablePrivateNetworking ? 'Disabled' : 'Enabled' storageServices: { resourceId: storage.id } diff --git a/deployers/bicep/postconfig.py b/deployers/bicep/postconfig.py index 5a12b56f..8d5b34af 100644 --- a/deployers/bicep/postconfig.py +++ b/deployers/bicep/postconfig.py @@ -1,14 +1,27 @@ +# postconfig.py from azure.cosmos import CosmosClient from azure.cosmos.exceptions import CosmosResourceNotFoundError -from azure.identity import DefaultAzureCredential +from azure.identity import DefaultAzureCredential, AzureAuthorityHosts from azure.keyvault.secrets import SecretClient import os import json -credential = DefaultAzureCredential() -token = credential.get_token("https://cosmos.azure.com/.default") + +def infer_authority(host_candidates): + """Infer Azure authority based on resource host (supports public and gov).""" + for host in host_candidates: + if host and ".azure.us" in host: + return AzureAuthorityHosts.AZURE_GOVERNMENT + return AzureAuthorityHosts.AZURE_PUBLIC_CLOUD + cosmosEndpoint = os.getenv("var_cosmosDb_uri") +keyVaultUri = os.getenv("var_keyVaultUri") +authority = infer_authority([cosmosEndpoint, keyVaultUri]) + +credential = DefaultAzureCredential(authority=authority) +token = credential.get_token("https://cosmos.azure.com/.default") + client = CosmosClient(cosmosEndpoint, credential=credential) database_name = "SimpleChat" @@ -32,7 +45,7 @@ # Get values from environment variables var_authenticationType = os.getenv("var_authenticationType") -var_keyVaultUri = os.getenv("var_keyVaultUri") +var_keyVaultUri = keyVaultUri var_openAIEndpoint = os.getenv("var_openAIEndpoint") var_openAIResourceGroup = os.getenv("var_openAIResourceGroup") From 72238701c5a7519cc9c60fc6bc59c3c8116c4381 Mon Sep 17 00:00:00 2001 From: SteveCInVA Date: Tue, 3 Mar 2026 21:00:50 +0000 Subject: [PATCH 2/8] fix parse error in ACCOUNT_NAME extraction. --- deployers/bicep/cosmosDb-postDeployPerms.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployers/bicep/cosmosDb-postDeployPerms.sh b/deployers/bicep/cosmosDb-postDeployPerms.sh index 4cf28f22..c24c691f 100644 --- a/deployers/bicep/cosmosDb-postDeployPerms.sh +++ b/deployers/bicep/cosmosDb-postDeployPerms.sh @@ -6,7 +6,7 @@ RG_NAME="${var_rgName}" COSMOS_URI="${var_cosmosDb_uri}" # Extract account name from commercial or sovereign (e.g., .azure.com, .azure.us), with optional port/path -ACCOUNT_NAME=$(echo "$COSMOS_URI" | sed -E 's#https://([^./]+)\.documents\.azure\.[^/:]+(:[0-9]+)?(/.*)?#$\1#') +ACCOUNT_NAME=$(echo "$COSMOS_URI" | sed -E 's#https://([^./]+)\.documents\.azure\.[^/:]+(:[0-9]+)?(/.*)?#\1#') echo "===============================" echo "Cosmos DB Account Name: $ACCOUNT_NAME" From 004824dc3fffdd85161ca5b4de76eefaf06b4f73 Mon Sep 17 00:00:00 2001 From: SteveCInVA Date: Wed, 4 Mar 2026 16:37:31 +0000 Subject: [PATCH 3/8] fix: update variable exports in postprovision hook for Azure deployment --- deployers/azure.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/deployers/azure.yaml b/deployers/azure.yaml index 0081ce4f..837abeef 100644 --- a/deployers/azure.yaml +++ b/deployers/azure.yaml @@ -26,8 +26,10 @@ hooks: echo "========================================" # Set up variables - export var_acrName=${var_acrname} + export var_configureApplication=${var_configureApplication} + export var_containerRegistry=${var_containerRegistry} + export var_imageName=${var_imageName} export var_cosmosDb_uri=${var_cosmosDb_uri} export var_cosmosDb_accountName=${var_cosmosDb_accountName} export var_subscriptionId=${AZURE_SUBSCRIPTION_ID} From 757cff6f8fff39af7c9a8bee677124bff0db7f80 Mon Sep 17 00:00:00 2001 From: SteveCInVA Date: Wed, 4 Mar 2026 16:38:01 +0000 Subject: [PATCH 4/8] fix: correct variable export for var_acrName in postprovision hook --- deployers/azure.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployers/azure.yaml b/deployers/azure.yaml index 837abeef..f0313731 100644 --- a/deployers/azure.yaml +++ b/deployers/azure.yaml @@ -26,7 +26,7 @@ hooks: echo "========================================" # Set up variables - + export var_acrName=${var_acrname} export var_configureApplication=${var_configureApplication} export var_containerRegistry=${var_containerRegistry} export var_imageName=${var_imageName} From c5a5f0d41e77a053b117f866127f79bec1089137 Mon Sep 17 00:00:00 2001 From: SteveCInVA Date: Wed, 4 Mar 2026 19:34:22 +0000 Subject: [PATCH 5/8] fix: update installation method for AZD in devContainer --- .devcontainer/devcontainer.json | 12 +++--------- .devcontainer/post-create.sh | 13 +++++++++++++ 2 files changed, 16 insertions(+), 9 deletions(-) create mode 100644 .devcontainer/post-create.sh diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index bf3f4cd0..dcaa125e 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -10,19 +10,13 @@ "VARIANT": "3.11" } }, - // Features to add to the dev container. More info: https://containers.dev/features. + // Features to add to the dev container. More info: . "features": { "ghcr.io/devcontainers/features/docker-in-docker:2": { "version": "latest" }, "ghcr.io/devcontainers/features/python:1": {}, "ghcr.io/devcontainers-extra/features/black:2": {}, - "ghcr.io/devcontainers/features/azure-cli:1": { - "installBicep": true, - "installUsingPython": true, - "version": "2.72.0", - "bicepVersion": "latest" - }, "ghcr.io/devcontainers/features/terraform:1": {}, "ghcr.io/devcontainers/features/powershell:1": {}, "ghcr.io/azure/azure-dev/azd:latest": {} @@ -30,7 +24,7 @@ // Use 'forwardPorts' to make a list of ports inside the container available locally. // "forwardPorts": [], // Use 'postCreateCommand' to run commands after the container is created. - "postCreateCommand": "python3 -m venv .venv && .venv/bin/pip install -r application/single_app/requirements.txt", + "postCreateCommand": ".devcontainer/post-create.sh", // Configure tool-specific properties. "customizations": { "vscode": { @@ -49,4 +43,4 @@ } // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. // "remoteUser": "root" -} +} \ No newline at end of file diff --git a/.devcontainer/post-create.sh b/.devcontainer/post-create.sh new file mode 100644 index 00000000..f721d160 --- /dev/null +++ b/.devcontainer/post-create.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +set -e + +sudo rm -f /usr/local/bin/az +sudo apt-get update +sudo apt-get install -y ca-certificates curl apt-transport-https lsb-release gnupg +curl -sL https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/microsoft.gpg > /dev/null +echo "deb [arch=amd64] https://packages.microsoft.com/repos/azure-cli/ $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/azure-cli.list +sudo apt-get update +sudo apt-get install -y azure-cli +python3 -m venv .venv +.venv/bin/pip install -r application/single_app/requirements.txt From d91708d271e5c2ee569b2e29049c8017f54ab6af Mon Sep 17 00:00:00 2001 From: SteveCInVA Date: Thu, 12 Mar 2026 13:46:28 +0000 Subject: [PATCH 6/8] fix: specify bash for postCreateCommand in devcontainer configuration --- .devcontainer/devcontainer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index dcaa125e..b59639e3 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -24,7 +24,7 @@ // Use 'forwardPorts' to make a list of ports inside the container available locally. // "forwardPorts": [], // Use 'postCreateCommand' to run commands after the container is created. - "postCreateCommand": ".devcontainer/post-create.sh", + "postCreateCommand": "bash .devcontainer/post-create.sh", // Configure tool-specific properties. "customizations": { "vscode": { From 59398576351427da071e64ec4859456305d1136e Mon Sep 17 00:00:00 2001 From: SteveCInVA Date: Fri, 20 Mar 2026 18:30:00 +0000 Subject: [PATCH 7/8] fix: update cloud environment handling for Azure resources and GPT models --- deployers/bicep/main.bicep | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/deployers/bicep/main.bicep b/deployers/bicep/main.bicep index 7e80bca0..99c1eff4 100644 --- a/deployers/bicep/main.bicep +++ b/deployers/bicep/main.bicep @@ -17,7 +17,7 @@ param location string ]) param cloudEnvironment string = az.environment().name == 'AzureCloud' ? 'public' : (az.environment().name == 'AzureUSGovernment' ? 'usgovernment' : 'custom') // SimpleChat expects public, usgovernment or custom -var scCloudEnvironment = cloudEnvironment == 'AzureCloud' ? 'public' : (cloudEnvironment == 'AzureUSGovernment' ? 'usgovernment' : cloudEnvironment) +var scCloudEnvironment = cloudEnvironment == 'public' ? 'public' : (cloudEnvironment == 'usgovernment' ? 'usgovernment' : cloudEnvironment) @description('''The name of the application to be deployed. - Name may only contain letters and numbers @@ -104,13 +104,29 @@ param gptModels array = [ { modelName: 'gpt-4.1' modelVersion: '2025-04-14' - skuName: cloudEnvironment == 'AzureCloud' ? 'GlobalStandard' : 'Standard' + skuName: 'GlobalStandard' + skuCapacity: 150 + } + { + modelName: 'gpt-4o' + modelVersion: '2024-11-20' + skuName: 'GlobalStandard' + skuCapacity: 150 + } +] + +@description('''Array of GPT model names to deploy to the OpenAI resource.''') +param gptModelsGov array = [ + { + modelName: 'gpt-4.1' + modelVersion: '2025-04-14' + skuName: 'Standard' skuCapacity: 50 } { modelName: 'gpt-4o' modelVersion: '2024-11-20' - skuName: cloudEnvironment == 'AzureCloud' ? 'GlobalStandard' : 'Standard' + skuName: 'Standard' skuCapacity: 50 } ] @@ -136,10 +152,9 @@ param embeddingModelsGov array = [ { modelName: 'text-embedding-ada-002' modelVersion: '2' - skuName: cloudEnvironment == 'AzureCloud' ? 'GlobalStandard' : 'Standard' + skuName: 'Standard' skuCapacity: 50 } - ] //---------------- @@ -179,7 +194,7 @@ param deployVideoIndexerService bool var rgName = '${appName}-${environment}-rg' var requiredTags = { application: appName, environment: environment, 'azd-env-name': azdEnvironmentName } var tags = union(requiredTags, specialTags) -var acrCloudSuffix = cloudEnvironment == 'AzureCloud' ? '.azurecr.io' : '.azurecr.us' +var acrCloudSuffix = cloudEnvironment == 'public' ? '.azurecr.io' : '.azurecr.us' var acrName = toLower('${appName}${environment}acr') var containerRegistry = '${acrName}${acrCloudSuffix}' var containerImageName = '${containerRegistry}/${imageName}' @@ -415,8 +430,8 @@ module openAI 'modules/openAI.bicep' = { authenticationType: authenticationType configureApplicationPermissions: configureApplicationPermissions - gptModels: gptModels - embeddingModels: cloudEnvironment == 'AzureCloud' ? embeddingModels : embeddingModelsGov + gptModels: cloudEnvironment == 'public' ? gptModels : gptModelsGov + embeddingModels: cloudEnvironment == 'public' ? embeddingModels : embeddingModelsGov enablePrivateNetworking: enablePrivateNetworking } @@ -644,6 +659,7 @@ module privateNetworking 'modules/privateNetworking.bicep' = if (enablePrivateNe // output values required for postprovision script in azure.yaml +output param_cloudEnvironment string = cloudEnvironment output var_acrName string = toLower('${appName}${environment}acr') output var_authenticationType string = toLower(authenticationType) output var_blobStorageEndpoint string = storageAccount.outputs.endpoint @@ -657,9 +673,9 @@ output var_documentIntelligenceServiceEndpoint string = docIntel.outputs.documen output var_keyVaultName string = keyVault.outputs.keyVaultName output var_keyVaultUri string = keyVault.outputs.keyVaultUri output var_openAIEndpoint string = openAI.outputs.openAIEndpoint -output var_openAIGPTModels array = gptModels +output var_openAIGPTModels array = cloudEnvironment == 'public' ? gptModels : gptModelsGov output var_openAIResourceGroup string = openAI.outputs.openAIResourceGroup //may be able to remove -output var_openAIEmbeddingModels array = embeddingModels +output var_openAIEmbeddingModels array = cloudEnvironment == 'public' ? embeddingModels : embeddingModelsGov #disable-next-line BCP318 // expect one value to be null output var_redisCacheHostName string = deployRedisCache ? redisCache.outputs.redisCacheHostName : '' output var_rgName string = rgName From 36fee06943260164b8be53690760b74578721cc8 Mon Sep 17 00:00:00 2001 From: SteveCInVA Date: Wed, 1 Apr 2026 19:48:42 +0000 Subject: [PATCH 8/8] fixes bug preventing deployment when private network is enabled --- deployers/bicep/modules/azureContainerRegistry.bicep | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/deployers/bicep/modules/azureContainerRegistry.bicep b/deployers/bicep/modules/azureContainerRegistry.bicep index d4ed9b6d..578ad925 100644 --- a/deployers/bicep/modules/azureContainerRegistry.bicep +++ b/deployers/bicep/modules/azureContainerRegistry.bicep @@ -30,10 +30,7 @@ resource acr 'Microsoft.ContainerRegistry/registries@2025-04-01' = { properties: { adminUserEnabled: true publicNetworkAccess: 'Enabled' // configuration is set in post provision step in azure.yaml with post deployment script - networkRuleSet: enablePrivateNetworking ? { - defaultAction: 'Deny' - ipRules: allowedIpAddresses - } : null + } tags: tags }