Skip to content

Commit 644f4ba

Browse files
Add quota validation scripts and update Azure configuration for AI model deployments
1 parent 0ff6ab4 commit 644f4ba

8 files changed

Lines changed: 416 additions & 25 deletions

azure.yaml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,19 @@ deployment:
2121
AzureAiServiceLocation: ${{ parameters.AzureAiServiceLocation }}
2222
Prefix: ${{ parameters.Prefix }}
2323
baseUrl: ${{ parameters.baseUrl }}
24+
hooks:
25+
preprovision:
26+
posix:
27+
shell: sh
28+
run: >
29+
chmod u+r+x ./scripts/validate_model_deployment_quota.sh; chmod u+r+x ./scripts/validate_model_quota.sh; ./scripts/validate_model_deployment_quota.sh --subscription "$AZURE_SUBSCRIPTION_ID" --location "${AZURE_AISERVICE_LOCATION:-japaneast}" --models-parameter "aiModelDeployments"
30+
interactive: false
31+
continueOnError: false
32+
33+
windows:
34+
shell: pwsh
35+
run: >
36+
$location = if ($env:AZURE_AISERVICE_LOCATION) { $env:AZURE_AISERVICE_LOCATION } else { "japaneast" };
37+
./scripts/validate_model_deployment_quota.ps1 -SubscriptionId $env:AZURE_SUBSCRIPTION_ID -Location $location -ModelsParameter "aiModelDeployments"
38+
interactive: false
39+
continueOnError: false

infra/main.bicep

Lines changed: 12 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,28 +4,19 @@ param Prefix string
44
var abbrs = loadJsonContent('./abbreviations.json')
55
var safePrefix = length(Prefix) > 20 ? substring(Prefix, 0, 20) : Prefix
66

7+
@description('Required. Location for all Resources except AI Foundry.')
8+
param solutionLocation string = resourceGroup().location
9+
710
@allowed([
811
'australiaeast'
9-
'brazilsouth'
10-
'canadacentral'
11-
'canadaeast'
1212
'eastus'
1313
'eastus2'
1414
'francecentral'
15-
'germanywestcentral'
1615
'japaneast'
17-
'koreacentral'
18-
'northcentralus'
1916
'norwayeast'
20-
'polandcentral'
21-
'southafricanorth'
22-
'southcentralus'
2317
'southindia'
2418
'swedencentral'
25-
'switzerlandnorth'
26-
'uaenorth'
2719
'uksouth'
28-
'westeurope'
2920
'westus'
3021
'westus3'
3122
])
@@ -57,8 +48,6 @@ param gptModelVersion string = '2024-08-06'
5748
var uniqueId = toLower(uniqueString(subscription().id, safePrefix, resourceGroup().location))
5849
var UniquePrefix = 'cm${padLeft(take(uniqueId, 12), 12, '0')}'
5950
var ResourcePrefix = take('cm${safePrefix}${UniquePrefix}', 15)
60-
var location = resourceGroup().location
61-
var dblocation = resourceGroup().location
6251
var cosmosdbDatabase = 'cmsadb'
6352
var cosmosdbBatchContainer = 'cmsabatch'
6453
var cosmosdbFileContainer = 'cmsafile'
@@ -85,7 +74,7 @@ var aiModelDeployments = [
8574

8675
resource azureAiServices 'Microsoft.CognitiveServices/accounts@2024-04-01-preview' = {
8776
name: azureAiServicesName
88-
location: location
77+
location: AzureAiServiceLocation
8978
sku: {
9079
name: 'S0'
9180
}
@@ -121,7 +110,7 @@ module managedIdentityModule 'deploy_managed_identity.bicep' = {
121110
params: {
122111
miName:'${abbrs.security.managedIdentity}${ResourcePrefix}'
123112
solutionName: ResourcePrefix
124-
solutionLocation: location
113+
solutionLocation: solutionLocation
125114
}
126115
scope: resourceGroup(resourceGroup().name)
127116
}
@@ -133,7 +122,7 @@ module kvault 'deploy_keyvault.bicep' = {
133122
params: {
134123
keyvaultName: '${abbrs.security.keyVault}${ResourcePrefix}'
135124
solutionName: ResourcePrefix
136-
solutionLocation: location
125+
solutionLocation: solutionLocation
137126
managedIdentityObjectId:managedIdentityModule.outputs.managedIdentityOutput.objectId
138127
}
139128
scope: resourceGroup(resourceGroup().name)
@@ -162,7 +151,7 @@ module containerAppsEnvironment 'br/public:avm/res/app/managed-environment:0.9.1
162151
params: {
163152
logAnalyticsWorkspaceResourceId: azureAifoundry.outputs.logAnalyticsId
164153
name: toLower('${ResourcePrefix}manenv')
165-
location: location
154+
location: solutionLocation
166155
zoneRedundant: false
167156
managedIdentities: managedIdentityModule
168157
}
@@ -175,7 +164,7 @@ module databaseAccount 'br/public:avm/res/document-db/database-account:0.9.0' =
175164
name: toLower('${abbrs.databases.cosmosDBDatabase}${ResourcePrefix}databaseAccount')
176165
// Non-required parameters
177166
enableAnalyticalStorage: true
178-
location: dblocation
167+
location: solutionLocation
179168
managedIdentities: {
180169
systemAssigned: true
181170
userAssignedResourceIds: [
@@ -193,7 +182,7 @@ module databaseAccount 'br/public:avm/res/document-db/database-account:0.9.0' =
193182
{
194183
failoverPriority: 0
195184
isZoneRedundant: false
196-
locationName: dblocation
185+
locationName: solutionLocation
197186
}
198187
]
199188
sqlDatabases: [
@@ -268,14 +257,14 @@ module containerAppFrontend 'br/public:avm/res/app/container-app:0.13.0' = {
268257
environmentResourceId: containerAppsEnvironment.outputs.resourceId
269258
name: toLower('${abbrs.containers.containerApp}${ResourcePrefix}Frontend')
270259
// Non-required parameters
271-
location: location
260+
location: solutionLocation
272261
}
273262
}
274263

275264

276265
resource containerAppBackend 'Microsoft.App/containerApps@2023-05-01' = {
277266
name: toLower('${abbrs.containers.containerApp}${ResourcePrefix}Backend')
278-
location: location
267+
location: solutionLocation
279268
identity: {
280269
type: 'SystemAssigned'
281270
}
@@ -393,7 +382,7 @@ resource containerAppBackend 'Microsoft.App/containerApps@2023-05-01' = {
393382
}
394383
resource storageContianerApp 'Microsoft.Storage/storageAccounts@2022-09-01' = {
395384
name: storageContainerName
396-
location: location
385+
location: solutionLocation
397386
sku: {
398387
name: storageSkuName
399388
}

infra/main.bicepparam

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
using './main.bicep'
22

33
param Prefix = readEnvironmentVariable('AZURE_ENV_NAME','azdtemp')
4-
param AzureAiServiceLocation = readEnvironmentVariable('AZURE_LOCATION','japaneast')
4+
param solutionLocation = readEnvironmentVariable('AZURE_LOCATION', 'eastus2')
5+
param AzureAiServiceLocation = readEnvironmentVariable('AZURE_AISERVICE_LOCATION','japaneast')
56
param capacity = int(readEnvironmentVariable('AZURE_ENV_MODEL_CAPACITY', '200'))
67
param deploymentType = readEnvironmentVariable('AZURE_ENV_MODEL_DEPLOYMENT_TYPE', 'GlobalStandard')
78
param llmModel = readEnvironmentVariable('AZURE_ENV_MODEL_NAME', 'gpt-4o')

infra/main.parameters.json

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,22 @@
5151
},
5252
"principalId": {
5353
"value": "${AZURE_PRINCIPAL_ID}"
54-
}
54+
},
55+
"aiModelDeployments": {
56+
"value": [
57+
{
58+
"name": "gpt-4o",
59+
"model": {
60+
"name": "gpt-4o",
61+
"version": "2024-08-06",
62+
"format": "OpenAI"
63+
},
64+
"sku": {
65+
"name": "GlobalStandard",
66+
"capacity": 150
67+
}
68+
}
69+
]
70+
}
5571
}
5672
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
param (
2+
[string]$SubscriptionId,
3+
[string]$Location,
4+
[string]$ModelsParameter
5+
)
6+
7+
# Verify all required parameters are provided
8+
$MissingParams = @()
9+
10+
if (-not $SubscriptionId) {
11+
$MissingParams += "subscription"
12+
}
13+
14+
if (-not $Location) {
15+
$MissingParams += "location"
16+
}
17+
18+
if (-not $ModelsParameter) {
19+
$MissingParams += "models-parameter"
20+
}
21+
22+
if ($MissingParams.Count -gt 0) {
23+
Write-Error "❌ ERROR: Missing required parameters: $($MissingParams -join ', ')"
24+
Write-Host "Usage: .\validate_model_deployment_quotas.ps1 -SubscriptionId <SUBSCRIPTION_ID> -Location <LOCATION> -ModelsParameter <MODELS_PARAMETER>"
25+
exit 1
26+
}
27+
28+
$JsonContent = Get-Content -Path "./infra/main.parameters.json" -Raw | ConvertFrom-Json
29+
30+
if (-not $JsonContent) {
31+
Write-Error "❌ ERROR: Failed to parse main.parameters.json. Ensure the JSON file is valid."
32+
exit 1
33+
}
34+
35+
$aiModelDeployments = $JsonContent.parameters.$ModelsParameter.value
36+
37+
if (-not $aiModelDeployments -or -not ($aiModelDeployments -is [System.Collections.IEnumerable])) {
38+
Write-Error "❌ ERROR: The specified property $ModelsParameter does not exist or is not an array."
39+
exit 1
40+
}
41+
42+
az account set --subscription $SubscriptionId
43+
Write-Host "🎯 Active Subscription: $(az account show --query '[name, id]' --output tsv)"
44+
45+
$QuotaAvailable = $true
46+
47+
foreach ($deployment in $aiModelDeployments) {
48+
$name = if ($env:AZURE_ENV_MODEL_NAME) { $env:AZURE_ENV_MODEL_NAME } else { $deployment.name }
49+
$model = if ($env:AZURE_ENV_MODEL_NAME) { $env:AZURE_ENV_MODEL_NAME } else { $deployment.model.name }
50+
$type = if ($env:AZURE_ENV_MODEL_DEPLOYMENT_TYPE) { $env:AZURE_ENV_MODEL_DEPLOYMENT_TYPE } else { $deployment.sku.name }
51+
$capacity = if ($env:AZURE_ENV_MODEL_CAPACITY) { $env:AZURE_ENV_MODEL_CAPACITY } else { $deployment.sku.capacity }
52+
53+
Write-Host "`n🔍 Validating model deployment: $name ..."
54+
& .\scripts\validate_model_quota.ps1 -Location $Location -Model $model -Capacity $capacity -DeploymentType $type
55+
$exitCode = $LASTEXITCODE
56+
57+
if ($exitCode -ne 0) {
58+
if ($exitCode -eq 2) {
59+
# Quota error already printed inside the script, exit gracefully without reprinting
60+
exit 1
61+
}
62+
Write-Error "❌ ERROR: Quota validation failed for model deployment: $name"
63+
$QuotaAvailable = $false
64+
}
65+
}
66+
67+
if (-not $QuotaAvailable) {
68+
Write-Error "❌ ERROR: One or more model deployments failed validation."
69+
exit 1
70+
} else {
71+
Write-Host "✅ All model deployments passed quota validation successfully."
72+
exit 0
73+
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
#!/bin/bash
2+
3+
SUBSCRIPTION_ID=""
4+
LOCATION=""
5+
MODELS_PARAMETER=""
6+
7+
while [[ $# -gt 0 ]]; do
8+
case "$1" in
9+
--subscription)
10+
SUBSCRIPTION_ID="$2"
11+
shift 2
12+
;;
13+
--location)
14+
LOCATION="$2"
15+
shift 2
16+
;;
17+
--models-parameter)
18+
MODELS_PARAMETER="$2"
19+
shift 2
20+
;;
21+
*)
22+
echo "Unknown option: $1"
23+
exit 1
24+
;;
25+
esac
26+
done
27+
28+
# Verify all required parameters are provided and echo missing ones
29+
MISSING_PARAMS=()
30+
31+
if [[ -z "$SUBSCRIPTION_ID" ]]; then
32+
MISSING_PARAMS+=("subscription")
33+
fi
34+
35+
if [[ -z "$LOCATION" ]]; then
36+
MISSING_PARAMS+=("location")
37+
fi
38+
39+
if [[ -z "$MODELS_PARAMETER" ]]; then
40+
MISSING_PARAMS+=("models-parameter")
41+
fi
42+
43+
if [[ ${#MISSING_PARAMS[@]} -ne 0 ]]; then
44+
echo "❌ ERROR: Missing required parameters: ${MISSING_PARAMS[*]}"
45+
echo "Usage: $0 --subscription <SUBSCRIPTION_ID> --location <LOCATION> --models-parameter <MODELS_PARAMETER>"
46+
exit 1
47+
fi
48+
49+
aiModelDeployments=$(jq -c ".parameters.$MODELS_PARAMETER.value[]" ./infra/main.parameters.json)
50+
51+
if [ $? -ne 0 ]; then
52+
echo "Error: Failed to parse main.parameters.json. Ensure jq is installed and the JSON file is valid."
53+
exit 1
54+
fi
55+
56+
az account set --subscription "$SUBSCRIPTION_ID"
57+
echo "🎯 Active Subscription: $(az account show --query '[name, id]' --output tsv)"
58+
59+
quotaAvailable=true
60+
61+
while IFS= read -r deployment; do
62+
name=${AZURE_ENV_MODEL_NAME:-$(echo "$deployment" | jq -r '.name')}
63+
model=${AZURE_ENV_MODEL_NAME:-$(echo "$deployment" | jq -r '.model.name')}
64+
type=${AZURE_ENV_MODEL_DEPLOYMENT_TYPE:-$(echo "$deployment" | jq -r '.sku.name')}
65+
capacity=${AZURE_ENV_MODEL_CAPACITY:-$(echo "$deployment" | jq -r '.sku.capacity')}
66+
67+
echo "🔍 Validating model deployment: $name ..."
68+
./scripts/validate_model_quota.sh --location "$LOCATION" --model "$model" --capacity $capacity --deployment-type $type
69+
70+
# Check if the script failed
71+
exit_code=$?
72+
if [ $exit_code -ne 0 ]; then
73+
if [ $exit_code -eq 2 ]; then
74+
# Skip printing any quota validation error — already handled inside the validation script
75+
exit 1
76+
fi
77+
echo "❌ ERROR: Quota validation failed for model deployment: $name"
78+
quotaAvailable=false
79+
fi
80+
done <<< "$(echo "$aiModelDeployments")"
81+
82+
if [ "$quotaAvailable" = false ]; then
83+
echo "❌ ERROR: One or more model deployments failed validation."
84+
exit 1
85+
else
86+
echo "✅ All model deployments passed quota validation successfully."
87+
exit 0
88+
fi

0 commit comments

Comments
 (0)