Skip to content

Commit 47fd2b4

Browse files
author
Shreyas-Microsoft
committed
Merge branch 'dev' into psl-sw-cm-fdp
2 parents 762baf2 + 90f97be commit 47fd2b4

11 files changed

Lines changed: 516 additions & 411 deletions

azure.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,14 @@ hooks:
2626
posix:
2727
shell: sh
2828
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
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 --SubscriptionId "$AZURE_SUBSCRIPTION_ID" --Location "${AZURE_AISERVICE_LOCATION:-japaneast}" --ModelsParameter "aiModelDeployments"
30+
interactive: true
3131
continueOnError: false
3232

3333
windows:
3434
shell: pwsh
3535
run: >
3636
$location = if ($env:AZURE_AISERVICE_LOCATION) { $env:AZURE_AISERVICE_LOCATION } else { "japaneast" };
3737
./scripts/validate_model_deployment_quota.ps1 -SubscriptionId $env:AZURE_SUBSCRIPTION_ID -Location $location -ModelsParameter "aiModelDeployments"
38-
interactive: false
38+
interactive: true
3939
continueOnError: false

docs/CustomizingAzdParameters.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ By default this template will use the environment name as the prefix to prevent
99
| Name | Type | Default Value | Purpose |
1010
| -------------------------------------- | ------- | ---------------- | ---------------------------------------------------------------------------------------------------- |
1111
| `AZURE_ENV_NAME` | string | `azdtemp` | Used as a prefix for all resource names to ensure uniqueness across environments. |
12-
| `AZURE_LOCATION` | string | `japaneast` | Location of the Azure resources. Controls where the infrastructure will be deployed. |
12+
| `AZURE_LOCATION` | string | `eastus2` | Location of the Azure resources. Controls where the infrastructure will be deployed. |
13+
| `AZURE_AISERVICE_LOCATION` | string | `japaneast` | Set the Azure AI Service Location. |
1314
| `AZURE_ENV_MODEL_DEPLOYMENT_TYPE` | string | `GlobalStandard` | Change the Model Deployment Type (allowed values: Standard, GlobalStandard). |
1415
| `AZURE_ENV_MODEL_NAME` | string | `gpt-4o` | Set the Model Name (allowed values: gpt-4o). |
1516
| `AZURE_ENV_MODEL_VERSION` | string | `2024-08-06` | Set the Azure model version (allowed values: 2024-08-06) |

docs/DeploymentGuide.md

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,18 @@ Check the [Azure Products by Region](https://azure.microsoft.com/en-us/explore/g
3636

3737
When you start the deployment, most parameters will have **default values**, but you can update the following settings by following the steps [here](../docs/CustomizingAzdParameters.md):
3838

39-
| **Setting** | **Description** | **Default value** |
40-
|------------|----------------| ------------|
41-
| **Azure Region** | The region where resources will be created. | East US|
42-
| **Resource Prefix** | Prefix for all resources created by this template. This prefix will be used to create unique names for all resources. The prefix must be unique within the resource group. | None |
43-
| **AI Location** | Location for all AI services resources. This location can be different from the resource group location | None |
44-
| **Capacity** | Configure capacity for **gpt-4o**. | 5k |
45-
| **Existing Log analytics workspace** | To reuse the existing Log analytics workspace Id. | |
39+
| **Setting** | **Description** | **Default value** |
40+
|----------------------------------|------------------------------------------------------------------------------------------------------|----------------------------|
41+
| **Azure Region** | The region where resources will be created. | East US |
42+
| **Resource Prefix** | Prefix for all resources created by this template. This prefix will be used to create unique names for all resources. The prefix must be unique within the resource group. | azdtemp |
43+
| **AI Location** | Location for all AI services resources. This location can be different from the resource group location. | japaneast |
44+
| **Capacity** | Configure capacity for **gpt-4o**. | 200 |
45+
| **Model Deployment Type** | Change the Model Deployment Type (allowed values: Standard, GlobalStandard). | GlobalStandard |
46+
| **Model Name** | Set the Model Name (allowed values: gpt-4o). | gpt-4o |
47+
| **Model Version** | Set the Azure model version (allowed values: 2024-08-06). | 2024-08-06 |
48+
| **Image Tag** | Set the Image tag (allowed values: latest, dev, hotfix). | latest |
49+
| **Existing Log analytics workspace** | To reuse the existing Log analytics workspace Id. | `<Existing Workspace Id>` |
50+
4651

4752
This accelerator can be configured to use authentication.
4853

infra/main.bicepparam

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ using './main.bicep'
33
param Prefix = readEnvironmentVariable('AZURE_ENV_NAME','azdtemp')
44
param solutionLocation = readEnvironmentVariable('AZURE_LOCATION', 'eastus2')
55
param AzureAiServiceLocation = readEnvironmentVariable('AZURE_AISERVICE_LOCATION','japaneast')
6-
param capacity = int(readEnvironmentVariable('AZURE_ENV_MODEL_CAPACITY', '200'))
6+
param capacity = int(readEnvironmentVariable('AZURE_ENV_MODEL_CAPACITY', '50'))
77
param deploymentType = readEnvironmentVariable('AZURE_ENV_MODEL_DEPLOYMENT_TYPE', 'GlobalStandard')
88
param llmModel = readEnvironmentVariable('AZURE_ENV_MODEL_NAME', 'gpt-4o')
99
param gptModelVersion = readEnvironmentVariable('AZURE_ENV_MODEL_VERSION', '2024-08-06')

infra/main.parameters.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@
6363
},
6464
"sku": {
6565
"name": "GlobalStandard",
66-
"capacity": 200
66+
"capacity": 50
6767
}
6868
}
6969
]

scripts/validate_model_deployment_quota.ps1

Lines changed: 59 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,41 +4,75 @@ param (
44
[string]$ModelsParameter
55
)
66

7-
# Verify all required parameters are provided
8-
$MissingParams = @()
9-
10-
if (-not $SubscriptionId) {
11-
$MissingParams += "subscription"
12-
}
7+
$AiFoundryName = $env:AZURE_AIFOUNDRY_NAME
8+
$ResourceGroup = $env:AZURE_RESOURCE_GROUP
139

14-
if (-not $Location) {
15-
$MissingParams += "location"
16-
}
17-
18-
if (-not $ModelsParameter) {
19-
$MissingParams += "models-parameter"
20-
}
10+
# Validate required parameters
11+
$MissingParams = @()
12+
if (-not $SubscriptionId) { $MissingParams += "SubscriptionId" }
13+
if (-not $Location) { $MissingParams += "Location" }
14+
if (-not $ModelsParameter) { $MissingParams += "ModelsParameter" }
2115

2216
if ($MissingParams.Count -gt 0) {
2317
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>"
18+
Write-Host "Usage: validate_model_deployment_quota.ps1 -SubscriptionId <SUBSCRIPTION_ID> -Location <LOCATION> -ModelsParameter <MODELS_PARAMETER>"
2519
exit 1
2620
}
2721

22+
# Load model deployments from parameter file
2823
$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."
24+
$aiModelDeployments = $JsonContent.parameters.$ModelsParameter.value
25+
if (-not $aiModelDeployments -or -not ($aiModelDeployments -is [System.Collections.IEnumerable])) {
26+
Write-Error "❌ ERROR: Failed to parse main.parameters.json or missing '$ModelsParameter'"
3227
exit 1
3328
}
3429

35-
$aiModelDeployments = $JsonContent.parameters.$ModelsParameter.value
30+
# Try to discover AI Foundry name if not set
31+
if (-not $AiFoundryName -and $ResourceGroup) {
32+
$AiFoundryName = az cognitiveservices account list `
33+
--resource-group $ResourceGroup `
34+
--query "sort_by([?kind=='AIServices'], &name)[0].name" `
35+
-o tsv 2>$null
36+
}
3637

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
38+
# Check if AI Foundry exists
39+
if ($AiFoundryName -and $ResourceGroup) {
40+
$existing = az cognitiveservices account show `
41+
--name $AiFoundryName `
42+
--resource-group $ResourceGroup `
43+
--query "name" --output tsv 2>$null
44+
45+
if ($existing) {
46+
# adding into .env
47+
azd env set AZURE_AIFOUNDRY_NAME $existing | Out-Null
48+
49+
$deployedModelsOutput = az cognitiveservices account deployment list `
50+
--name $AiFoundryName `
51+
--resource-group $ResourceGroup `
52+
--query "[].name" --output tsv 2>$null
53+
54+
$deployedModels = @()
55+
if ($deployedModelsOutput -is [string]) {
56+
$deployedModels += $deployedModelsOutput
57+
} elseif ($deployedModelsOutput) {
58+
$deployedModels = $deployedModelsOutput -split "`r?`n"
59+
}
60+
61+
$requiredDeployments = $aiModelDeployments | ForEach-Object { $_.name }
62+
$missingDeployments = $requiredDeployments | Where-Object { $_ -notin $deployedModels }
63+
64+
if ($missingDeployments.Count -eq 0) {
65+
Write-Host "ℹ️ AI Foundry '$AiFoundryName' exists and all required model deployments are already provisioned."
66+
Write-Host "⏭️ Skipping quota validation."
67+
exit 0
68+
} else {
69+
Write-Host "🔍 AI Foundry exists, but the following model deployments are missing: $($missingDeployments -join ', ')"
70+
Write-Host "➡️ Proceeding with quota validation for missing models..."
71+
}
72+
}
4073
}
4174

75+
# Run quota validation
4276
az account set --subscription $SubscriptionId
4377
Write-Host "🎯 Active Subscription: $(az account show --query '[name, id]' --output tsv)"
4478

@@ -50,13 +84,13 @@ foreach ($deployment in $aiModelDeployments) {
5084
$type = if ($env:AZURE_ENV_MODEL_DEPLOYMENT_TYPE) { $env:AZURE_ENV_MODEL_DEPLOYMENT_TYPE } else { $deployment.sku.name }
5185
$capacity = if ($env:AZURE_ENV_MODEL_CAPACITY) { $env:AZURE_ENV_MODEL_CAPACITY } else { $deployment.sku.capacity }
5286

53-
Write-Host "`n🔍 Validating model deployment: $name ..."
87+
Write-Host ""
88+
Write-Host "🔍 Validating model deployment: $name ..."
5489
& .\scripts\validate_model_quota.ps1 -Location $Location -Model $model -Capacity $capacity -DeploymentType $type
5590
$exitCode = $LASTEXITCODE
5691

5792
if ($exitCode -ne 0) {
5893
if ($exitCode -eq 2) {
59-
# Quota error already printed inside the script, exit gracefully without reprinting
6094
exit 1
6195
}
6296
Write-Error "❌ ERROR: Quota validation failed for model deployment: $name"
@@ -65,9 +99,9 @@ foreach ($deployment in $aiModelDeployments) {
6599
}
66100

67101
if (-not $QuotaAvailable) {
68-
Write-Error "❌ ERROR: One or more model deployments failed validation."
102+
Write-Error "❌ ERROR: One or more model deployments failed quota validation."
69103
exit 1
70104
} else {
71105
Write-Host "✅ All model deployments passed quota validation successfully."
72106
exit 0
73-
}
107+
}

scripts/validate_model_deployment_quota.sh

Lines changed: 74 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -6,83 +6,118 @@ MODELS_PARAMETER=""
66

77
while [[ $# -gt 0 ]]; do
88
case "$1" in
9-
--subscription)
9+
--SubscriptionId)
1010
SUBSCRIPTION_ID="$2"
1111
shift 2
1212
;;
13-
--location)
13+
--Location)
1414
LOCATION="$2"
1515
shift 2
1616
;;
17-
--models-parameter)
17+
--ModelsParameter)
1818
MODELS_PARAMETER="$2"
1919
shift 2
2020
;;
2121
*)
22-
echo "Unknown option: $1"
22+
echo "❌ ERROR: Unknown option: $1"
2323
exit 1
2424
;;
2525
esac
2626
done
2727

28-
# Verify all required parameters are provided and echo missing ones
28+
AIFOUNDRY_NAME="${AZURE_AIFOUNDRY_NAME}"
29+
RESOURCE_GROUP="${AZURE_RESOURCE_GROUP}"
30+
31+
# Validate required parameters
2932
MISSING_PARAMS=()
33+
[[ -z "$SUBSCRIPTION_ID" ]] && MISSING_PARAMS+=("SubscriptionId")
34+
[[ -z "$LOCATION" ]] && MISSING_PARAMS+=("Location")
35+
[[ -z "$MODELS_PARAMETER" ]] && MISSING_PARAMS+=("ModelsParameter")
3036

31-
if [[ -z "$SUBSCRIPTION_ID" ]]; then
32-
MISSING_PARAMS+=("subscription")
37+
if [[ ${#MISSING_PARAMS[@]} -ne 0 ]]; then
38+
echo "❌ ERROR: Missing required parameters: ${MISSING_PARAMS[*]}"
39+
echo "Usage: $0 --SubscriptionId <SUBSCRIPTION_ID> --Location <LOCATION> --ModelsParameter <MODELS_PARAMETER>"
40+
exit 1
3341
fi
3442

35-
if [[ -z "$LOCATION" ]]; then
36-
MISSING_PARAMS+=("location")
43+
# Load model definitions
44+
aiModelDeployments=$(jq -c ".parameters.$MODELS_PARAMETER.value[]" ./infra/main.parameters.json 2>/dev/null)
45+
if [[ $? -ne 0 || -z "$aiModelDeployments" ]]; then
46+
echo "❌ ERROR: Failed to parse main.parameters.json or missing '$MODELS_PARAMETER'"
47+
exit 1
3748
fi
3849

39-
if [[ -z "$MODELS_PARAMETER" ]]; then
40-
MISSING_PARAMS+=("models-parameter")
50+
# Try to discover AI Foundry name if not set
51+
if [[ -z "$AIFOUNDRY_NAME" && -n "$RESOURCE_GROUP" ]]; then
52+
AIFOUNDRY_NAME=$(az cognitiveservices account list --resource-group "$RESOURCE_GROUP" \
53+
--query "sort_by([?kind=='AIServices'], &name)[0].name" -o tsv 2>/dev/null)
4154
fi
4255

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
56+
# Check if AI Foundry exists
57+
if [[ -n "$AIFOUNDRY_NAME" && -n "$RESOURCE_GROUP" ]]; then
58+
existing=$(az cognitiveservices account show --name "$AIFOUNDRY_NAME" \
59+
--resource-group "$RESOURCE_GROUP" --query "name" --output tsv 2>/dev/null)
4860

49-
aiModelDeployments=$(jq -c ".parameters.$MODELS_PARAMETER.value[]" ./infra/main.parameters.json)
61+
if [[ -n "$existing" ]]; then
62+
# adding into .env
63+
azd env set AZURE_AIFOUNDRY_NAME "$existing" > /dev/null
5064

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
65+
# Check model deployments
66+
existing_deployments=$(az cognitiveservices account deployment list \
67+
--name "$AIFOUNDRY_NAME" \
68+
--resource-group "$RESOURCE_GROUP" \
69+
--query "[].name" --output tsv 2>/dev/null)
70+
71+
required_models=$(jq -r ".parameters.$MODELS_PARAMETER.value[].name" ./infra/main.parameters.json)
72+
73+
missing_models=()
74+
for model in $required_models; do
75+
if ! grep -q -w "$model" <<< "$existing_deployments"; then
76+
missing_models+=("$model")
77+
fi
78+
done
79+
80+
if [[ ${#missing_models[@]} -eq 0 ]]; then
81+
echo "ℹ️ AI Foundry '$AIFOUNDRY_NAME' exists and all required model deployments are already provisioned."
82+
echo "⏭️ Skipping quota validation."
83+
exit 0
84+
else
85+
echo "🔍 AI Foundry exists, but the following model deployments are missing: ${missing_models[*]}"
86+
echo "➡️ Proceeding with quota validation for missing models..."
87+
fi
88+
fi
5489
fi
5590

91+
# Run quota validation
5692
az account set --subscription "$SUBSCRIPTION_ID"
5793
echo "🎯 Active Subscription: $(az account show --query '[name, id]' --output tsv)"
5894

5995
quotaAvailable=true
6096

6197
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')}
98+
name=${AZURE_ENV_MODEL_NAME:-$(echo "$deployment" | jq -r '.name')}
99+
model=${AZURE_ENV_MODEL_NAME:-$(echo "$deployment" | jq -r '.model.name')}
100+
type=${AZURE_ENV_MODEL_DEPLOYMENT_TYPE:-$(echo "$deployment" | jq -r '.sku.name')}
101+
capacity=${AZURE_ENV_MODEL_CAPACITY:-$(echo "$deployment" | jq -r '.sku.capacity')}
66102

103+
echo ""
67104
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
105+
./scripts/validate_model_quota.sh --location "$LOCATION" --model "$model" --capacity "$capacity" --deployment-type "$type"
71106
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
107+
108+
if [[ $exit_code -ne 0 ]]; then
109+
if [[ $exit_code -eq 2 ]]; then
110+
exit 1
111+
fi
112+
echo "❌ ERROR: Quota validation failed for model deployment: $name"
113+
quotaAvailable=false
79114
fi
80115
done <<< "$(echo "$aiModelDeployments")"
81116

82-
if [ "$quotaAvailable" = false ]; then
83-
echo "❌ ERROR: One or more model deployments failed validation."
84-
exit 1
117+
if [[ "$quotaAvailable" = false ]]; then
118+
echo "❌ ERROR: One or more model deployments failed quota validation."
119+
exit 1
85120
else
86-
echo "✅ All model deployments passed quota validation successfully."
87-
exit 0
121+
echo "✅ All model deployments passed quota validation successfully."
122+
exit 0
88123
fi

0 commit comments

Comments
 (0)