From 03269f56d9ced1be7fb49fb24f6415bd525161d8 Mon Sep 17 00:00:00 2001 From: Shreyas-Microsoft Date: Tue, 24 Jun 2025 17:08:51 +0530 Subject: [PATCH 1/4] remove the quota check validation --- scripts/validate_model_deployment_quota.ps1 | 85 ++----- scripts/validate_model_deployment_quota.sh | 116 +++------ scripts/validate_model_quota.ps1 | 219 +++++------------ scripts/validate_model_quota.sh | 257 +++++--------------- 4 files changed, 182 insertions(+), 495 deletions(-) diff --git a/scripts/validate_model_deployment_quota.ps1 b/scripts/validate_model_deployment_quota.ps1 index dda7297..3ff4ab9 100644 --- a/scripts/validate_model_deployment_quota.ps1 +++ b/scripts/validate_model_deployment_quota.ps1 @@ -4,93 +4,58 @@ param ( [string]$ModelsParameter ) -$AiFoundryName = $env:AZURE_AIFOUNDRY_NAME -$ResourceGroup = $env:AZURE_RESOURCE_GROUP - -# Validate required parameters +# Verify all required parameters are provided $MissingParams = @() -if (-not $SubscriptionId) { $MissingParams += "SubscriptionId" } -if (-not $Location) { $MissingParams += "Location" } -if (-not $ModelsParameter) { $MissingParams += "ModelsParameter" } -if ($MissingParams.Count -gt 0) { - Write-Error "❌ ERROR: Missing required parameters: $($MissingParams -join ', ')" - Write-Host "Usage: validate_model_deployment_quota.ps1 -SubscriptionId -Location -ModelsParameter " - exit 1 +if (-not $SubscriptionId) { + $MissingParams += "subscription" } -# Load model deployments from parameter file -$JsonContent = Get-Content -Path "./infra/main.parameters.json" -Raw | ConvertFrom-Json -$aiModelDeployments = $JsonContent.parameters.$ModelsParameter.value -if (-not $aiModelDeployments -or -not ($aiModelDeployments -is [System.Collections.IEnumerable])) { - Write-Error "❌ ERROR: Failed to parse main.parameters.json or missing '$ModelsParameter'" - exit 1 +if (-not $Location) { + $MissingParams += "location" } -# Try to discover AI Foundry name if not set -if (-not $AiFoundryName -and $ResourceGroup) { - $AiFoundryName = az cognitiveservices account list ` - --resource-group $ResourceGroup ` - --query "sort_by([?kind=='AIServices'], &name)[0].name" ` - -o tsv 2>$null +if (-not $ModelsParameter) { + $MissingParams += "models-parameter" } -# Check if AI Foundry exists -if ($AiFoundryName -and $ResourceGroup) { - $existing = az cognitiveservices account show ` - --name $AiFoundryName ` - --resource-group $ResourceGroup ` - --query "name" --output tsv 2>$null - - if ($existing) { - # adding into .env - azd env set AZURE_AIFOUNDRY_NAME $existing | Out-Null +if ($MissingParams.Count -gt 0) { + Write-Error "❌ ERROR: Missing required parameters: $($MissingParams -join ', ')" + Write-Host "Usage: .\validate_model_deployment_quotas.ps1 -SubscriptionId -Location -ModelsParameter " + exit 1 +} - $deployedModelsOutput = az cognitiveservices account deployment list ` - --name $AiFoundryName ` - --resource-group $ResourceGroup ` - --query "[].name" --output tsv 2>$null +$JsonContent = Get-Content -Path "./infra/main.parameters.json" -Raw | ConvertFrom-Json - $deployedModels = @() - if ($deployedModelsOutput -is [string]) { - $deployedModels += $deployedModelsOutput - } elseif ($deployedModelsOutput) { - $deployedModels = $deployedModelsOutput -split "`r?`n" - } +if (-not $JsonContent) { + Write-Error "❌ ERROR: Failed to parse main.parameters.json. Ensure the JSON file is valid." + exit 1 +} - $requiredDeployments = $aiModelDeployments | ForEach-Object { $_.name } - $missingDeployments = $requiredDeployments | Where-Object { $_ -notin $deployedModels } +$aiModelDeployments = $JsonContent.parameters.$ModelsParameter.value - if ($missingDeployments.Count -eq 0) { - Write-Host "ℹ️ AI Foundry '$AiFoundryName' exists and all required model deployments are already provisioned." - Write-Host "⏭️ Skipping quota validation." - exit 0 - } else { - Write-Host "🔍 AI Foundry exists, but the following model deployments are missing: $($missingDeployments -join ', ')" - Write-Host "➡️ Proceeding with quota validation for missing models..." - } - } +if (-not $aiModelDeployments -or -not ($aiModelDeployments -is [System.Collections.IEnumerable])) { + Write-Error "❌ ERROR: The specified property $ModelsParameter does not exist or is not an array." + exit 1 } -# Run quota validation az account set --subscription $SubscriptionId Write-Host "🎯 Active Subscription: $(az account show --query '[name, id]' --output tsv)" $QuotaAvailable = $true - foreach ($deployment in $aiModelDeployments) { $name = if ($env:AZURE_ENV_MODEL_NAME) { $env:AZURE_ENV_MODEL_NAME } else { $deployment.name } $model = if ($env:AZURE_ENV_MODEL_NAME) { $env:AZURE_ENV_MODEL_NAME } else { $deployment.model.name } $type = if ($env:AZURE_ENV_MODEL_DEPLOYMENT_TYPE) { $env:AZURE_ENV_MODEL_DEPLOYMENT_TYPE } else { $deployment.sku.name } $capacity = if ($env:AZURE_ENV_MODEL_CAPACITY) { $env:AZURE_ENV_MODEL_CAPACITY } else { $deployment.sku.capacity } - Write-Host "" - Write-Host "🔍 Validating model deployment: $name ..." + Write-Host "`n🔍 Validating model deployment: $name ..." & .\scripts\validate_model_quota.ps1 -Location $Location -Model $model -Capacity $capacity -DeploymentType $type $exitCode = $LASTEXITCODE if ($exitCode -ne 0) { if ($exitCode -eq 2) { + # Quota error already printed inside the script, exit gracefully without reprinting exit 1 } Write-Error "❌ ERROR: Quota validation failed for model deployment: $name" @@ -99,9 +64,9 @@ foreach ($deployment in $aiModelDeployments) { } if (-not $QuotaAvailable) { - Write-Error "❌ ERROR: One or more model deployments failed quota validation." + Write-Error "❌ ERROR: One or more model deployments failed validation." exit 1 } else { Write-Host "✅ All model deployments passed quota validation successfully." exit 0 -} +} \ No newline at end of file diff --git a/scripts/validate_model_deployment_quota.sh b/scripts/validate_model_deployment_quota.sh index acf1e3b..a16b520 100644 --- a/scripts/validate_model_deployment_quota.sh +++ b/scripts/validate_model_deployment_quota.sh @@ -1,123 +1,87 @@ #!/bin/bash - SUBSCRIPTION_ID="" LOCATION="" MODELS_PARAMETER="" while [[ $# -gt 0 ]]; do case "$1" in - --SubscriptionId) + --subscription) SUBSCRIPTION_ID="$2" shift 2 ;; - --Location) + --location) LOCATION="$2" shift 2 ;; - --ModelsParameter) + --models-parameter) MODELS_PARAMETER="$2" shift 2 ;; *) - echo "❌ ERROR: Unknown option: $1" + echo "Unknown option: $1" exit 1 ;; esac done -AIFOUNDRY_NAME="${AZURE_AIFOUNDRY_NAME}" -RESOURCE_GROUP="${AZURE_RESOURCE_GROUP}" - -# Validate required parameters +# Verify all required parameters are provided and echo missing ones MISSING_PARAMS=() -[[ -z "$SUBSCRIPTION_ID" ]] && MISSING_PARAMS+=("SubscriptionId") -[[ -z "$LOCATION" ]] && MISSING_PARAMS+=("Location") -[[ -z "$MODELS_PARAMETER" ]] && MISSING_PARAMS+=("ModelsParameter") -if [[ ${#MISSING_PARAMS[@]} -ne 0 ]]; then - echo "❌ ERROR: Missing required parameters: ${MISSING_PARAMS[*]}" - echo "Usage: $0 --SubscriptionId --Location --ModelsParameter " - exit 1 +if [[ -z "$SUBSCRIPTION_ID" ]]; then + MISSING_PARAMS+=("subscription") fi -# Load model definitions -aiModelDeployments=$(jq -c ".parameters.$MODELS_PARAMETER.value[]" ./infra/main.parameters.json 2>/dev/null) -if [[ $? -ne 0 || -z "$aiModelDeployments" ]]; then - echo "❌ ERROR: Failed to parse main.parameters.json or missing '$MODELS_PARAMETER'" - exit 1 +if [[ -z "$LOCATION" ]]; then + MISSING_PARAMS+=("location") fi -# Try to discover AI Foundry name if not set -if [[ -z "$AIFOUNDRY_NAME" && -n "$RESOURCE_GROUP" ]]; then - AIFOUNDRY_NAME=$(az cognitiveservices account list --resource-group "$RESOURCE_GROUP" \ - --query "sort_by([?kind=='AIServices'], &name)[0].name" -o tsv 2>/dev/null) +if [[ -z "$MODELS_PARAMETER" ]]; then + MISSING_PARAMS+=("models-parameter") fi -# Check if AI Foundry exists -if [[ -n "$AIFOUNDRY_NAME" && -n "$RESOURCE_GROUP" ]]; then - existing=$(az cognitiveservices account show --name "$AIFOUNDRY_NAME" \ - --resource-group "$RESOURCE_GROUP" --query "name" --output tsv 2>/dev/null) - - if [[ -n "$existing" ]]; then - # adding into .env - azd env set AZURE_AIFOUNDRY_NAME "$existing" > /dev/null - - # Check model deployments - existing_deployments=$(az cognitiveservices account deployment list \ - --name "$AIFOUNDRY_NAME" \ - --resource-group "$RESOURCE_GROUP" \ - --query "[].name" --output tsv 2>/dev/null) +if [[ ${#MISSING_PARAMS[@]} -ne 0 ]]; then + echo "❌ ERROR: Missing required parameters: ${MISSING_PARAMS[*]}" + echo "Usage: $0 --subscription --location --models-parameter " + exit 1 +fi - required_models=$(jq -r ".parameters.$MODELS_PARAMETER.value[].name" ./infra/main.parameters.json) +aiModelDeployments=$(jq -c ".parameters.$MODELS_PARAMETER.value[]" ./infra/main.parameters.json) - missing_models=() - for model in $required_models; do - if ! grep -q -w "$model" <<< "$existing_deployments"; then - missing_models+=("$model") - fi - done - - if [[ ${#missing_models[@]} -eq 0 ]]; then - echo "ℹ️ AI Foundry '$AIFOUNDRY_NAME' exists and all required model deployments are already provisioned." - echo "⏭️ Skipping quota validation." - exit 0 - else - echo "🔍 AI Foundry exists, but the following model deployments are missing: ${missing_models[*]}" - echo "➡️ Proceeding with quota validation for missing models..." - fi - fi +if [ $? -ne 0 ]; then + echo "Error: Failed to parse main.parameters.json. Ensure jq is installed and the JSON file is valid." + exit 1 fi -# Run quota validation az account set --subscription "$SUBSCRIPTION_ID" echo "🎯 Active Subscription: $(az account show --query '[name, id]' --output tsv)" quotaAvailable=true while IFS= read -r deployment; do - name=${AZURE_ENV_MODEL_NAME:-$(echo "$deployment" | jq -r '.name')} - model=${AZURE_ENV_MODEL_NAME:-$(echo "$deployment" | jq -r '.model.name')} - type=${AZURE_ENV_MODEL_DEPLOYMENT_TYPE:-$(echo "$deployment" | jq -r '.sku.name')} - capacity=${AZURE_ENV_MODEL_CAPACITY:-$(echo "$deployment" | jq -r '.sku.capacity')} + name=${AZURE_ENV_MODEL_NAME:-$(echo "$deployment" | jq -r '.name')} + model=${AZURE_ENV_MODEL_NAME:-$(echo "$deployment" | jq -r '.model.name')} + type=${AZURE_ENV_MODEL_DEPLOYMENT_TYPE:-$(echo "$deployment" | jq -r '.sku.name')} + capacity=${AZURE_ENV_MODEL_CAPACITY:-$(echo "$deployment" | jq -r '.sku.capacity')} - echo "" echo "🔍 Validating model deployment: $name ..." - ./scripts/validate_model_quota.sh --location "$LOCATION" --model "$model" --capacity "$capacity" --deployment-type "$type" - exit_code=$? + ./scripts/validate_model_quota.sh --location "$LOCATION" --model "$model" --capacity $capacity --deployment-type $type - if [[ $exit_code -ne 0 ]]; then - if [[ $exit_code -eq 2 ]]; then - exit 1 - fi - echo "❌ ERROR: Quota validation failed for model deployment: $name" - quotaAvailable=false + # Check if the script failed + exit_code=$? + if [ $exit_code -ne 0 ]; then + if [ $exit_code -eq 2 ]; then + # Skip printing any quota validation error — already handled inside the validation script + exit 1 + fi + echo "❌ ERROR: Quota validation failed for model deployment: $name" + quotaAvailable=false fi done <<< "$(echo "$aiModelDeployments")" -if [[ "$quotaAvailable" = false ]]; then - echo "❌ ERROR: One or more model deployments failed quota validation." - exit 1 +if [ "$quotaAvailable" = false ]; then + echo "❌ ERROR: One or more model deployments failed validation." + exit 1 else - echo "✅ All model deployments passed quota validation successfully." - exit 0 -fi + echo "✅ All model deployments passed quota validation successfully." + exit 0 +fi \ No newline at end of file diff --git a/scripts/validate_model_quota.ps1 b/scripts/validate_model_quota.ps1 index e151628..d9056f0 100644 --- a/scripts/validate_model_quota.ps1 +++ b/scripts/validate_model_quota.ps1 @@ -5,199 +5,104 @@ param ( [int]$Capacity ) -$RECOMMENDED_TOKENS = 200 -$BicepParamsFile = "main.bicepparams" -$ParametersJsonFile = "./infra/main.parameters.json" -$PreferredRegions = @('australiaeast', 'eastus', 'eastus2', 'francecentral', 'japaneast', 'norwayeast', 'southindia', 'swedencentral', 'uksouth', 'westus', 'westus3') -$AllResults = @() -$RecommendedRegions = @() -$NotRecommendedRegions = @() -$EligibleFallbacks = @() - -# ------------------ Validate Inputs ------------------ +# Verify required parameters $MissingParams = @() if (-not $Location) { $MissingParams += "location" } if (-not $Model) { $MissingParams += "model" } -if (-not $Capacity -or $Capacity -le 0) { $MissingParams += "capacity" } +if (-not $Capacity) { $MissingParams += "capacity" } +if (-not $DeploymentType) { $MissingParams += "deployment-type" } if ($MissingParams.Count -gt 0) { - Write-Error "❌ ERROR: Missing or invalid parameters: $($MissingParams -join ', ')" + Write-Error "❌ ERROR: Missing required parameters: $($MissingParams -join ', ')" Write-Host "Usage: .\validate_model_quota.ps1 -Location -Model -Capacity [-DeploymentType ]" exit 1 } if ($DeploymentType -ne "Standard" -and $DeploymentType -ne "GlobalStandard") { - Write-Error "❌ ERROR: Invalid deployment type: $DeploymentType. Allowed: 'Standard', 'GlobalStandard'" + Write-Error "❌ ERROR: Invalid deployment type: $DeploymentType. Allowed values are 'Standard' or 'GlobalStandard'." exit 1 } $ModelType = "OpenAI.$DeploymentType.$Model" +$PreferredRegions = @('australiaeast', 'eastus', 'eastus2', 'francecentral', 'japaneast', 'norwayeast', 'southindia', 'swedencentral', 'uksouth', 'westus', 'westus3') +$AllResults = @() + function Check-Quota { - param ([string]$Region) + param ( + [string]$Region + ) + + $ModelInfoRaw = az cognitiveservices usage list --location $Region --query "[?name.value=='$ModelType']" --output json + $ModelInfo = $null try { - $ModelInfoRaw = az cognitiveservices usage list --location $Region --query "[?name.value=='$ModelType']" --output json 2>$null $ModelInfo = $ModelInfoRaw | ConvertFrom-Json - if (-not $ModelInfo -or $ModelInfo.Count -eq 0) { - return $null - } - - $Current = [int]$ModelInfo[0].currentValue - $Limit = [int]$ModelInfo[0].limit - $Available = $Limit - $Current - - if ($Available -ge $RECOMMENDED_TOKENS) { - $script:RecommendedRegions += $Region - } else { - $script:NotRecommendedRegions += $Region - } - - return [PSCustomObject]@{ - Region = $Region - Model = $ModelType - Limit = $Limit - Used = $Current - Available = $Available - } } catch { - return $null + return } -} -function Show-Table { - Write-Host "`n--------------------------------------------------------------------------------------------------" - Write-Host "| No. | Region | Model Name | Limit | Used | Available |" - Write-Host "--------------------------------------------------------------------------------------------------" - $i = 1 - foreach ($entry in $AllResults | Where-Object { $_.Available -gt 50 }) { - Write-Host ("| {0,-3} | {1,-15} | {2,-35} | {3,-5} | {4,-5} | {5,-9} |" -f $i, $entry.Region, $entry.Model, $entry.Limit, $entry.Used, $entry.Available) - $i++ + if (-not $ModelInfo) { + return } - Write-Host "--------------------------------------------------------------------------------------------------" -} -function Set-DeploymentValues($Region, $Capacity) { - azd env set AZURE_AISERVICE_LOCATION "$Region" | Out-Null - azd env set AZURE_ENV_MODEL_CAPACITY "$Capacity" | Out-Null - - if (Test-Path $ParametersJsonFile) { - try { - $json = Get-Content $ParametersJsonFile -Raw | ConvertFrom-Json - if ($json.parameters.aiModelDeployments.value.Count -gt 0) { - $json.parameters.aiModelDeployments.value[0].sku.capacity = $Capacity - $json | ConvertTo-Json -Depth 20 | Set-Content $ParametersJsonFile -Force - Write-Host "✅ Updated '$ParametersJsonFile' with capacity $Capacity." - } else { - Write-Host "⚠️ 'aiModelDeployments.value' array is empty. No changes made." - } - } catch { - Write-Host "❌ Failed to update '$ParametersJsonFile': $_" - } - } else { - Write-Host "⚠️ '$ParametersJsonFile' not found. Skipping update." - } -} + $CurrentValue = ($ModelInfo | Where-Object { $_.name.value -eq $ModelType }).currentValue + $Limit = ($ModelInfo | Where-Object { $_.name.value -eq $ModelType }).limit -# ------------------ Check Primary Region ------------------ -Write-Host "`n🔍 Checking quota in the requested region '$Location'..." -$PrimaryResult = Check-Quota -Region $Location - -if ($PrimaryResult) { - $AllResults += $PrimaryResult - if ($PrimaryResult.Available -ge $Capacity) { - if ($RecommendedRegions -notcontains $Location -and $RecommendedRegions.Count -gt 0) { - Write-Host "`n⚠️ Selected region '$Location' has sufficient quota but is not among the recommended regions (≥ $RECOMMENDED_TOKENS tokens)." - Write-Host "🚨 Your application may not work as expected due to limited quota." - Write-Host "`nℹ️ Recommended regions: $($RecommendedRegions -join ', ')" - Write-Host "👉 It's advisable to deploy in one of these regions for optimal app performance." - $choice = Read-Host "❓ Do you want to choose a recommended region instead? (y/n)" - if ($choice -match "^[Yy]$") { - Show-Table - break - } else { - if ($Capacity -gt 200) { - Write-Host "`n⚠️ Reducing capacity to 200 in '$BicepParamsFile' for safer deployment..." - (Get-Content $BicepParamsFile) -replace "capacity\s*:\s*\d+", "capacity: 200" | Set-Content $BicepParamsFile - Write-Host "✅ Updated '$BicepParamsFile' with capacity 200." - } - Set-DeploymentValues $Location $Capacity - Write-Host "✅ Proceeding with '$Location' as selected." - exit 0 - } - } else { - Write-Host "`n✅ Sufficient quota found in original region '$Location'." - Set-DeploymentValues $Location $Capacity - exit 0 - } - } else { - Write-Host "`n⚠️ Insufficient quota in '$Location'. Checking fallback regions..." + $CurrentValue = [int]($CurrentValue -replace '\.0+$', '') + $Limit = [int]($Limit -replace '\.0+$', '') + $Available = $Limit - $CurrentValue + + $script:AllResults += [PSCustomObject]@{ + Region = $Region + Model = $ModelType + Limit = $Limit + Used = $CurrentValue + Available = $Available } -} else { - Write-Host "`n⚠️ Could not retrieve quota info for region '$Location'." } -# ------------------ Check Fallback Regions ------------------ foreach ($region in $PreferredRegions) { - if ($region -eq $Location) { continue } - $result = Check-Quota -Region $region - if ($result) { - $AllResults += $result - if ($result.Available -ge $Capacity) { - $EligibleFallbacks += $region - } - } + Check-Quota -Region $region } -Show-Table - -if ($EligibleFallbacks.Count -gt 0) { - Write-Host "`n➡️ Found fallback regions with sufficient quota." - if ($RecommendedRegions.Count -gt 0) { - Write-Host "`nℹ️ Recommended regions (≥ $RECOMMENDED_TOKENS tokens available):" - foreach ($region in $RecommendedRegions) { - Write-Host " - $region" - } - } +# Display Results Table +Write-Host "\n-------------------------------------------------------------------------------------------------------------" +Write-Host "| No. | Region | Model Name | Limit | Used | Available |" +Write-Host "-------------------------------------------------------------------------------------------------------------" + +$count = 1 +foreach ($entry in $AllResults) { + $index = $PreferredRegions.IndexOf($entry.Region) + 1 + $modelShort = $entry.Model.Substring($entry.Model.LastIndexOf(".") + 1) + Write-Host ("| {0,-4} | {1,-16} | {2,-35} | {3,-7} | {4,-7} | {5,-9} |" -f $index, $entry.Region, $entry.Model, $entry.Limit, $entry.Used, $entry.Available) + $count++ } +Write-Host "-------------------------------------------------------------------------------------------------------------" -# ------------------ Manual Prompt if No Quota Found ------------------ -Write-Host "`n❌ ERROR: No region has sufficient quota." - -while ($true) { - $ManualRegion = Read-Host "`nPlease enter a region you want to try manually" - if (-not $ManualRegion) { - Write-Host "❌ ERROR: No region entered. Exiting." - exit 1 - } +$EligibleRegion = $AllResults | Where-Object { $_.Region -eq $Location -and $_.Available -ge $Capacity } +if ($EligibleRegion) { + Write-Host "\n✅ Sufficient quota found in original region '$Location'." + exit 0 +} - $ManualCapacityStr = Read-Host "Enter the capacity you want to use (numeric value)" - if (-not ($ManualCapacityStr -as [int]) -or [int]$ManualCapacityStr -le 0) { - Write-Host "❌ Invalid capacity value. Try again." - continue - } +$FallbackRegions = $AllResults | Where-Object { $_.Region -ne $Location -and $_.Available -ge $Capacity } - $ManualCapacity = [int]$ManualCapacityStr - $ManualResult = Check-Quota -Region $ManualRegion +if ($FallbackRegions.Count -gt 0) { + Write-Host "`n❌ Deployment cannot proceed because the original region '$Location' lacks sufficient quota." + Write-Host "➡️ You can retry using one of the following regions with sufficient quota:`n" - if (-not $ManualResult) { - Write-Host "⚠️ Could not retrieve quota info for region '$ManualRegion'. Try again." - continue + foreach ($region in $FallbackRegions) { + Write-Host " • $($region.Region) (Available: $($region.Available))" } - if ($ManualResult.Available -ge $ManualCapacity) { - if ($ManualResult.Available -lt $RECOMMENDED_TOKENS) { - Write-Host "`n⚠️ Region '$ManualRegion' has less than recommended ($RECOMMENDED_TOKENS) tokens." - $proceed = Read-Host "❓ Proceed anyway? (y/n)" - if ($proceed -notmatch "^[Yy]$") { - continue - } - } - - Set-DeploymentValues $ManualRegion $ManualCapacity - Write-Host "✅ Deployment values set. Exiting." - exit 0 - } else { - Write-Host "❌ Quota in region '$ManualRegion' is insufficient. Available: $($ManualResult.Available), Required: $ManualCapacity" - } + Write-Host "`n🔧 To proceed, run:" + Write-Host " azd env set AZURE_AISERVICE_LOCATION ''" + Write-Host "📌 To confirm it's set correctly, run:" + Write-Host " azd env get-value AZURE_AISERVICE_LOCATION" + Write-Host "▶️ Once confirmed, re-run azd up to deploy the model in the new region." + exit 2 } + +Write-Error "❌ ERROR: No available quota found in any region." +exit 1 diff --git a/scripts/validate_model_quota.sh b/scripts/validate_model_quota.sh index b1ca2bf..3512b6d 100644 --- a/scripts/validate_model_quota.sh +++ b/scripts/validate_model_quota.sh @@ -1,246 +1,99 @@ #!/bin/bash - LOCATION="" MODEL="" DEPLOYMENT_TYPE="Standard" CAPACITY=0 -RECOMMENDED_TOKENS=200 ALL_REGIONS=('australiaeast' 'eastus' 'eastus2' 'francecentral' 'japaneast' 'norwayeast' 'southindia' 'swedencentral' 'uksouth' 'westus' 'westus3') -# Globals for recommended/not recommended regions -RECOMMENDED_REGIONS=() -NOT_RECOMMENDED_REGIONS=() -ALL_RESULTS=() -FALLBACK_RESULTS=() - -# -------------------- Utility: Update .env and main.parameters.json -------------------- -update_env_and_parameters() { - local new_location="$1" - local new_capacity="$2" - - echo "➡️ Updating environment and parameters with Location='$new_location' and Capacity='$new_capacity'..." - - # Update the AZD environment - azd env set AZURE_AISERVICE_LOCATION "$new_location" - azd env set AZURE_ENV_MODEL_CAPACITY "$new_capacity" - - # Update main.parameters.json - local PARAM_FILE="./infra/main.parameters.json" - if [[ ! -f "$PARAM_FILE" ]]; then - echo "❌ ERROR: $PARAM_FILE not found, cannot update parameters." - return 1 - fi - - jq --arg loc "$new_location" \ - '.parameters.location.value = $loc' "$PARAM_FILE" > "${PARAM_FILE}.tmp" && mv "${PARAM_FILE}.tmp" "$PARAM_FILE" - - jq --argjson cap "$new_capacity" --arg model "$MODEL" \ - '(.parameters.aiModelDeployments.value[] | select(.name == $model) | .sku.capacity) |= $cap' "$PARAM_FILE" > "${PARAM_FILE}.tmp" && mv "${PARAM_FILE}.tmp" "$PARAM_FILE" - - echo "✅ Updated .env and $PARAM_FILE successfully." -} - -# -------------------- Function: Check Quota -------------------- -check_quota() { - local region="$1" - local MODEL_TYPE="OpenAI.$DEPLOYMENT_TYPE.$MODEL" - local output - - output=$(az cognitiveservices usage list --location "$region" --query "[?name.value=='$MODEL_TYPE']" --output json 2>/dev/null) - - if [[ -z "$output" || "$output" == "[]" ]]; then - return 2 # No data - fi - - local CURRENT_VALUE=$(echo "$output" | jq -r '.[0].currentValue // 0' | cut -d'.' -f1) - local LIMIT=$(echo "$output" | jq -r '.[0].limit // 0' | cut -d'.' -f1) - local AVAILABLE=$((LIMIT - CURRENT_VALUE)) - - ALL_RESULTS+=("$region|$LIMIT|$CURRENT_VALUE|$AVAILABLE") - - if [[ "$AVAILABLE" -ge "$RECOMMENDED_TOKENS" ]]; then - RECOMMENDED_REGIONS+=("$region") - else - NOT_RECOMMENDED_REGIONS+=("$region") - fi - - if [[ "$AVAILABLE" -ge "$CAPACITY" ]]; then - return 0 - else - return 1 - fi -} - -# -------------------- Input Validation -------------------- while [[ $# -gt 0 ]]; do case "$1" in --model) - MODEL="$2"; shift 2 ;; + MODEL="$2" + shift 2 + ;; --capacity) - CAPACITY="$2"; shift 2 ;; + CAPACITY="$2" + shift 2 + ;; --deployment-type) - DEPLOYMENT_TYPE="$2"; shift 2 ;; + DEPLOYMENT_TYPE="$2" + shift 2 + ;; --location) - LOCATION="$2"; shift 2 ;; + LOCATION="$2" + shift 2 + ;; *) - echo "❌ ERROR: Unknown option: $1"; exit 1 ;; + echo "Unknown option: $1" + exit 1 + ;; esac done +# Validate required params +MISSING_PARAMS=() [[ -z "$LOCATION" ]] && MISSING_PARAMS+=("location") [[ -z "$MODEL" ]] && MISSING_PARAMS+=("model") -if ! [[ "$CAPACITY" =~ ^[0-9]+$ ]] || [[ "$CAPACITY" -le 0 ]]; then - MISSING_PARAMS+=("capacity") -fi +[[ -z "$CAPACITY" ]] && MISSING_PARAMS+=("capacity") if [[ ${#MISSING_PARAMS[@]} -ne 0 ]]; then - echo "❌ ERROR: Missing/invalid: ${MISSING_PARAMS[*]}" + echo "❌ ERROR: Missing required parameters: ${MISSING_PARAMS[*]}" echo "Usage: $0 --location --model --capacity [--deployment-type ]" exit 1 fi if [[ "$DEPLOYMENT_TYPE" != "Standard" && "$DEPLOYMENT_TYPE" != "GlobalStandard" ]]; then - echo "❌ ERROR: Invalid deployment type: $DEPLOYMENT_TYPE" + echo "❌ ERROR: Invalid deployment type: $DEPLOYMENT_TYPE. Allowed values are 'Standard' or 'GlobalStandard'." exit 1 fi -# -------------------- Main Logic Starts -------------------- -echo "🔍 Checking quota in '$LOCATION' for model '$MODEL'..." +MODEL_TYPE="OpenAI.$DEPLOYMENT_TYPE.$MODEL" -check_quota "$LOCATION" -primary_status=$? +declare -a FALLBACK_REGIONS=() +ROW_NO=1 -if [[ $primary_status -eq 1 ]]; then - primary_entry="${ALL_RESULTS[0]}" - IFS='|' read -r _ limit used available <<< "$primary_entry" - echo -e "\n⚠️ Insufficient quota in '$LOCATION' (Available: $available, Required: $CAPACITY). Checking fallback regions..." -fi +printf "\n%-5s | %-20s | %-40s | %-10s | %-10s | %-10s\n" "No." "Region" "Model Name" "Limit" "Used" "Available" +printf -- "---------------------------------------------------------------------------------------------------------------------\n" for region in "${ALL_REGIONS[@]}"; do - [[ "$region" == "$LOCATION" ]] && continue - check_quota "$region" - [[ $? -eq 0 ]] && FALLBACK_RESULTS+=("$region") -done + MODEL_INFO=$(az cognitiveservices usage list --location "$region" --query "[?name.value=='$MODEL_TYPE']" --output json 2>/dev/null) -# -------------------- Quota Table Output -------------------- -echo "" -printf "%-5s | %-16s | %-33s | %-6s | %-6s | %-9s\n" "No." "Region" "Model Name" "Limit" "Used" "Available" -printf -- "---------------------------------------------------------------------------------------------\n" - -index=1 -REGIONS_WITH_QUOTA=() -for result in "${ALL_RESULTS[@]}"; do - IFS='|' read -r region limit used available <<< "$result" - if (( available >= 50 )); then - printf "| %-3s | %-16s | %-33s | %-6s | %-6s | %-9s |\n" "$index" "$region" "OpenAI.$DEPLOYMENT_TYPE.$MODEL" "$limit" "$used" "$available" - REGIONS_WITH_QUOTA+=("$region|$available") - ((index++)) - fi -done -printf -- "---------------------------------------------------------------------------------------------\n" - -# -------------------- Prompt if No Region Has Enough -------------------- -if [[ $primary_status -ne 0 && ${#FALLBACK_RESULTS[@]} -eq 0 ]]; then - echo -e "\n❌ No region has sufficient quota (≥ $CAPACITY tokens)." - - max_available=0; max_region="" - for result in "${ALL_RESULTS[@]}"; do - IFS='|' read -r region limit used available <<< "$result" - if (( available > max_available )); then - max_available=$available - max_region=$region - fi - done + if [[ -n "$MODEL_INFO" && "$MODEL_INFO" != "[]" ]]; then + CURRENT_VALUE=$(echo "$MODEL_INFO" | jq -r '.[0].currentValue // 0' | cut -d'.' -f1) + LIMIT=$(echo "$MODEL_INFO" | jq -r '.[0].limit // 0' | cut -d'.' -f1) + AVAILABLE=$((LIMIT - CURRENT_VALUE)) - if (( max_available == 0 )); then - echo "⚠️ No quota info from any region. Cannot proceed." - exit 1 - fi + printf "%-5s | %-20s | %-40s | %-10s | %-10s | %-10s\n" "$ROW_NO" "$region" "$MODEL_TYPE" "$LIMIT" "$CURRENT_VALUE" "$AVAILABLE" - echo "➡️ Highest available quota: $max_available tokens in '$max_region'." - echo -n "❓ Enter new capacity to use (<= $max_available): " - read -r new_capacity < /dev/tty + if [[ "$region" == "$LOCATION" && "$AVAILABLE" -ge "$CAPACITY" ]]; then + echo -e "\n✅ Sufficient quota available in user-specified region: $LOCATION" + exit 0 + fi - if ! [[ "$new_capacity" =~ ^[0-9]+$ ]] || (( new_capacity > max_available )) || (( new_capacity <= 0 )); then - echo "❌ Invalid capacity entered. Exiting." - exit 1 + if [[ "$region" != "$LOCATION" && "$AVAILABLE" -ge "$CAPACITY" ]]; then + FALLBACK_REGIONS+=("$region ($AVAILABLE)") + fi fi - echo -n "❓ Enter location to use (default: $max_region): " - read -r new_location < /dev/tty - new_location="${new_location:-$max_region}" - - CAPACITY=$new_capacity - LOCATION=$new_location + ((ROW_NO++)) +done - check_quota "$LOCATION" - [[ $? -eq 0 ]] || { echo "❌ Insufficient quota in '$LOCATION'. Exiting."; exit 1; } +printf -- "---------------------------------------------------------------------------------------------------------------------\n" - update_env_and_parameters "$LOCATION" "$CAPACITY" - echo "✅ Deployment settings updated." - exit 0 +if [[ "${#FALLBACK_REGIONS[@]}" -gt 0 ]]; then + echo -e "\n❌ Deployment cannot proceed because the original region '$LOCATION' lacks sufficient quota." + echo "➡️ You can retry using one of the following regions with sufficient quota:" + for fallback in "${FALLBACK_REGIONS[@]}"; do + echo " • $fallback" + done + echo -e "\n🔧 To proceed, run:" + echo " azd env set AZURE_AISERVICE_LOCATION ''" + echo "📌 To confirm it's set correctly, run:" + echo " azd env get-value AZURE_AISERVICE_LOCATION" + echo "▶️ Once confirmed, re-run azd up to deploy the model in the new region." + exit 2 fi -# -------------------- Handle Fallback Prompt -------------------- -ask_for_location() { - echo -e "\nPlease choose a region from the above list:" - echo -n "📍 Enter region: " - read -r new_location < /dev/tty - - if [[ -z "$new_location" ]]; then - echo "❌ ERROR: No location entered. Exiting." - exit 1 - fi - - echo -n "🔢 Enter capacity (tokens): " - read -r new_capacity < /dev/tty - - if ! [[ "$new_capacity" =~ ^[0-9]+$ ]] || (( new_capacity <= 0 )); then - echo "❌ Invalid capacity entered." - ask_for_location - return - fi - - CAPACITY=$new_capacity - LOCATION=$new_location - - check_quota "$LOCATION" - if [[ $? -eq 0 ]]; then - update_env_and_parameters "$LOCATION" "$CAPACITY" - echo "✅ Updated and ready to deploy in '$LOCATION'." - exit 0 - else - echo "❌ Insufficient quota in '$LOCATION'. Try another." - ask_for_location - fi -} - -# -------------------- Final Decision Logic -------------------- -if [[ $primary_status -eq 0 ]]; then - - if [[ " ${NOT_RECOMMENDED_REGIONS[*]} " == *" $LOCATION "* ]]; then - recommended_list=$(IFS=, ; echo "${RECOMMENDED_REGIONS[*]}") - bold_regions=$(printf "\033[1m%s\033[0m" "$recommended_list") - echo -e "\n⚠️ \033[1mWarning:\033[0m Region '$LOCATION' has available tokens less than the recommended threshold ($RECOMMENDED_TOKENS)." - echo -e "🚨 Your application may not work as expected due to limited quota." - echo -e "\nℹ️ Recommended regions (≥ $RECOMMENDED_TOKENS tokens available): $bold_regions" - echo -e "👉 It's advisable to deploy in one of these regions for optimal app performance." - - echo -n "❓ Proceed anyway? (y/n): " - read -r proceed < /dev/tty - if [[ "$proceed" =~ ^[Yy]$ ]]; then - update_env_and_parameters "$LOCATION" "$CAPACITY" - echo "✅ Proceeding with '$LOCATION'." - exit 0 - else - ask_for_location - fi - else - update_env_and_parameters "$LOCATION" "$CAPACITY" - echo "✅ Quota is sufficient in '$LOCATION'. Proceeding." - exit 0 - fi -else - ask_for_location -fi +echo "❌ ERROR: No available quota found in any of the fallback regions." +exit 1 \ No newline at end of file From ecccbfbc184133be304e8f372376b93f7f5df1f5 Mon Sep 17 00:00:00 2001 From: Shreyas-Microsoft Date: Tue, 24 Jun 2025 17:19:52 +0530 Subject: [PATCH 2/4] remove preprovision step --- azure.yaml | 16 --- scripts/validate_model_deployment_quota.ps1 | 72 ------------- scripts/validate_model_deployment_quota.sh | 87 ---------------- scripts/validate_model_quota.ps1 | 108 -------------------- scripts/validate_model_quota.sh | 99 ------------------ 5 files changed, 382 deletions(-) delete mode 100644 scripts/validate_model_deployment_quota.ps1 delete mode 100644 scripts/validate_model_deployment_quota.sh delete mode 100644 scripts/validate_model_quota.ps1 delete mode 100644 scripts/validate_model_quota.sh diff --git a/azure.yaml b/azure.yaml index 5dc6faf..fbf47cd 100644 --- a/azure.yaml +++ b/azure.yaml @@ -21,19 +21,3 @@ deployment: AzureAiServiceLocation: ${{ parameters.AzureAiServiceLocation }} Prefix: ${{ parameters.Prefix }} baseUrl: ${{ parameters.baseUrl }} -hooks: - preprovision: - posix: - shell: sh - run: > - 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" - interactive: true - continueOnError: false - - windows: - shell: pwsh - run: > - $location = if ($env:AZURE_AISERVICE_LOCATION) { $env:AZURE_AISERVICE_LOCATION } else { "japaneast" }; - ./scripts/validate_model_deployment_quota.ps1 -SubscriptionId $env:AZURE_SUBSCRIPTION_ID -Location $location -ModelsParameter "aiModelDeployments" - interactive: true - continueOnError: false \ No newline at end of file diff --git a/scripts/validate_model_deployment_quota.ps1 b/scripts/validate_model_deployment_quota.ps1 deleted file mode 100644 index 3ff4ab9..0000000 --- a/scripts/validate_model_deployment_quota.ps1 +++ /dev/null @@ -1,72 +0,0 @@ -param ( - [string]$SubscriptionId, - [string]$Location, - [string]$ModelsParameter -) - -# Verify all required parameters are provided -$MissingParams = @() - -if (-not $SubscriptionId) { - $MissingParams += "subscription" -} - -if (-not $Location) { - $MissingParams += "location" -} - -if (-not $ModelsParameter) { - $MissingParams += "models-parameter" -} - -if ($MissingParams.Count -gt 0) { - Write-Error "❌ ERROR: Missing required parameters: $($MissingParams -join ', ')" - Write-Host "Usage: .\validate_model_deployment_quotas.ps1 -SubscriptionId -Location -ModelsParameter " - exit 1 -} - -$JsonContent = Get-Content -Path "./infra/main.parameters.json" -Raw | ConvertFrom-Json - -if (-not $JsonContent) { - Write-Error "❌ ERROR: Failed to parse main.parameters.json. Ensure the JSON file is valid." - exit 1 -} - -$aiModelDeployments = $JsonContent.parameters.$ModelsParameter.value - -if (-not $aiModelDeployments -or -not ($aiModelDeployments -is [System.Collections.IEnumerable])) { - Write-Error "❌ ERROR: The specified property $ModelsParameter does not exist or is not an array." - exit 1 -} - -az account set --subscription $SubscriptionId -Write-Host "🎯 Active Subscription: $(az account show --query '[name, id]' --output tsv)" - -$QuotaAvailable = $true -foreach ($deployment in $aiModelDeployments) { - $name = if ($env:AZURE_ENV_MODEL_NAME) { $env:AZURE_ENV_MODEL_NAME } else { $deployment.name } - $model = if ($env:AZURE_ENV_MODEL_NAME) { $env:AZURE_ENV_MODEL_NAME } else { $deployment.model.name } - $type = if ($env:AZURE_ENV_MODEL_DEPLOYMENT_TYPE) { $env:AZURE_ENV_MODEL_DEPLOYMENT_TYPE } else { $deployment.sku.name } - $capacity = if ($env:AZURE_ENV_MODEL_CAPACITY) { $env:AZURE_ENV_MODEL_CAPACITY } else { $deployment.sku.capacity } - - Write-Host "`n🔍 Validating model deployment: $name ..." - & .\scripts\validate_model_quota.ps1 -Location $Location -Model $model -Capacity $capacity -DeploymentType $type - $exitCode = $LASTEXITCODE - - if ($exitCode -ne 0) { - if ($exitCode -eq 2) { - # Quota error already printed inside the script, exit gracefully without reprinting - exit 1 - } - Write-Error "❌ ERROR: Quota validation failed for model deployment: $name" - $QuotaAvailable = $false - } -} - -if (-not $QuotaAvailable) { - Write-Error "❌ ERROR: One or more model deployments failed validation." - exit 1 -} else { - Write-Host "✅ All model deployments passed quota validation successfully." - exit 0 -} \ No newline at end of file diff --git a/scripts/validate_model_deployment_quota.sh b/scripts/validate_model_deployment_quota.sh deleted file mode 100644 index a16b520..0000000 --- a/scripts/validate_model_deployment_quota.sh +++ /dev/null @@ -1,87 +0,0 @@ -#!/bin/bash -SUBSCRIPTION_ID="" -LOCATION="" -MODELS_PARAMETER="" - -while [[ $# -gt 0 ]]; do - case "$1" in - --subscription) - SUBSCRIPTION_ID="$2" - shift 2 - ;; - --location) - LOCATION="$2" - shift 2 - ;; - --models-parameter) - MODELS_PARAMETER="$2" - shift 2 - ;; - *) - echo "Unknown option: $1" - exit 1 - ;; - esac -done - -# Verify all required parameters are provided and echo missing ones -MISSING_PARAMS=() - -if [[ -z "$SUBSCRIPTION_ID" ]]; then - MISSING_PARAMS+=("subscription") -fi - -if [[ -z "$LOCATION" ]]; then - MISSING_PARAMS+=("location") -fi - -if [[ -z "$MODELS_PARAMETER" ]]; then - MISSING_PARAMS+=("models-parameter") -fi - -if [[ ${#MISSING_PARAMS[@]} -ne 0 ]]; then - echo "❌ ERROR: Missing required parameters: ${MISSING_PARAMS[*]}" - echo "Usage: $0 --subscription --location --models-parameter " - exit 1 -fi - -aiModelDeployments=$(jq -c ".parameters.$MODELS_PARAMETER.value[]" ./infra/main.parameters.json) - -if [ $? -ne 0 ]; then - echo "Error: Failed to parse main.parameters.json. Ensure jq is installed and the JSON file is valid." - exit 1 -fi - -az account set --subscription "$SUBSCRIPTION_ID" -echo "🎯 Active Subscription: $(az account show --query '[name, id]' --output tsv)" - -quotaAvailable=true - -while IFS= read -r deployment; do - name=${AZURE_ENV_MODEL_NAME:-$(echo "$deployment" | jq -r '.name')} - model=${AZURE_ENV_MODEL_NAME:-$(echo "$deployment" | jq -r '.model.name')} - type=${AZURE_ENV_MODEL_DEPLOYMENT_TYPE:-$(echo "$deployment" | jq -r '.sku.name')} - capacity=${AZURE_ENV_MODEL_CAPACITY:-$(echo "$deployment" | jq -r '.sku.capacity')} - - echo "🔍 Validating model deployment: $name ..." - ./scripts/validate_model_quota.sh --location "$LOCATION" --model "$model" --capacity $capacity --deployment-type $type - - # Check if the script failed - exit_code=$? - if [ $exit_code -ne 0 ]; then - if [ $exit_code -eq 2 ]; then - # Skip printing any quota validation error — already handled inside the validation script - exit 1 - fi - echo "❌ ERROR: Quota validation failed for model deployment: $name" - quotaAvailable=false - fi -done <<< "$(echo "$aiModelDeployments")" - -if [ "$quotaAvailable" = false ]; then - echo "❌ ERROR: One or more model deployments failed validation." - exit 1 -else - echo "✅ All model deployments passed quota validation successfully." - exit 0 -fi \ No newline at end of file diff --git a/scripts/validate_model_quota.ps1 b/scripts/validate_model_quota.ps1 deleted file mode 100644 index d9056f0..0000000 --- a/scripts/validate_model_quota.ps1 +++ /dev/null @@ -1,108 +0,0 @@ -param ( - [string]$Location, - [string]$Model, - [string]$DeploymentType = "Standard", - [int]$Capacity -) - -# Verify required parameters -$MissingParams = @() -if (-not $Location) { $MissingParams += "location" } -if (-not $Model) { $MissingParams += "model" } -if (-not $Capacity) { $MissingParams += "capacity" } -if (-not $DeploymentType) { $MissingParams += "deployment-type" } - -if ($MissingParams.Count -gt 0) { - Write-Error "❌ ERROR: Missing required parameters: $($MissingParams -join ', ')" - Write-Host "Usage: .\validate_model_quota.ps1 -Location -Model -Capacity [-DeploymentType ]" - exit 1 -} - -if ($DeploymentType -ne "Standard" -and $DeploymentType -ne "GlobalStandard") { - Write-Error "❌ ERROR: Invalid deployment type: $DeploymentType. Allowed values are 'Standard' or 'GlobalStandard'." - exit 1 -} - -$ModelType = "OpenAI.$DeploymentType.$Model" - -$PreferredRegions = @('australiaeast', 'eastus', 'eastus2', 'francecentral', 'japaneast', 'norwayeast', 'southindia', 'swedencentral', 'uksouth', 'westus', 'westus3') -$AllResults = @() - -function Check-Quota { - param ( - [string]$Region - ) - - $ModelInfoRaw = az cognitiveservices usage list --location $Region --query "[?name.value=='$ModelType']" --output json - $ModelInfo = $null - - try { - $ModelInfo = $ModelInfoRaw | ConvertFrom-Json - } catch { - return - } - - if (-not $ModelInfo) { - return - } - - $CurrentValue = ($ModelInfo | Where-Object { $_.name.value -eq $ModelType }).currentValue - $Limit = ($ModelInfo | Where-Object { $_.name.value -eq $ModelType }).limit - - $CurrentValue = [int]($CurrentValue -replace '\.0+$', '') - $Limit = [int]($Limit -replace '\.0+$', '') - $Available = $Limit - $CurrentValue - - $script:AllResults += [PSCustomObject]@{ - Region = $Region - Model = $ModelType - Limit = $Limit - Used = $CurrentValue - Available = $Available - } -} - -foreach ($region in $PreferredRegions) { - Check-Quota -Region $region -} - -# Display Results Table -Write-Host "\n-------------------------------------------------------------------------------------------------------------" -Write-Host "| No. | Region | Model Name | Limit | Used | Available |" -Write-Host "-------------------------------------------------------------------------------------------------------------" - -$count = 1 -foreach ($entry in $AllResults) { - $index = $PreferredRegions.IndexOf($entry.Region) + 1 - $modelShort = $entry.Model.Substring($entry.Model.LastIndexOf(".") + 1) - Write-Host ("| {0,-4} | {1,-16} | {2,-35} | {3,-7} | {4,-7} | {5,-9} |" -f $index, $entry.Region, $entry.Model, $entry.Limit, $entry.Used, $entry.Available) - $count++ -} -Write-Host "-------------------------------------------------------------------------------------------------------------" - -$EligibleRegion = $AllResults | Where-Object { $_.Region -eq $Location -and $_.Available -ge $Capacity } -if ($EligibleRegion) { - Write-Host "\n✅ Sufficient quota found in original region '$Location'." - exit 0 -} - -$FallbackRegions = $AllResults | Where-Object { $_.Region -ne $Location -and $_.Available -ge $Capacity } - -if ($FallbackRegions.Count -gt 0) { - Write-Host "`n❌ Deployment cannot proceed because the original region '$Location' lacks sufficient quota." - Write-Host "➡️ You can retry using one of the following regions with sufficient quota:`n" - - foreach ($region in $FallbackRegions) { - Write-Host " • $($region.Region) (Available: $($region.Available))" - } - - Write-Host "`n🔧 To proceed, run:" - Write-Host " azd env set AZURE_AISERVICE_LOCATION ''" - Write-Host "📌 To confirm it's set correctly, run:" - Write-Host " azd env get-value AZURE_AISERVICE_LOCATION" - Write-Host "▶️ Once confirmed, re-run azd up to deploy the model in the new region." - exit 2 -} - -Write-Error "❌ ERROR: No available quota found in any region." -exit 1 diff --git a/scripts/validate_model_quota.sh b/scripts/validate_model_quota.sh deleted file mode 100644 index 3512b6d..0000000 --- a/scripts/validate_model_quota.sh +++ /dev/null @@ -1,99 +0,0 @@ -#!/bin/bash -LOCATION="" -MODEL="" -DEPLOYMENT_TYPE="Standard" -CAPACITY=0 - -ALL_REGIONS=('australiaeast' 'eastus' 'eastus2' 'francecentral' 'japaneast' 'norwayeast' 'southindia' 'swedencentral' 'uksouth' 'westus' 'westus3') - -while [[ $# -gt 0 ]]; do - case "$1" in - --model) - MODEL="$2" - shift 2 - ;; - --capacity) - CAPACITY="$2" - shift 2 - ;; - --deployment-type) - DEPLOYMENT_TYPE="$2" - shift 2 - ;; - --location) - LOCATION="$2" - shift 2 - ;; - *) - echo "Unknown option: $1" - exit 1 - ;; - esac -done - -# Validate required params -MISSING_PARAMS=() -[[ -z "$LOCATION" ]] && MISSING_PARAMS+=("location") -[[ -z "$MODEL" ]] && MISSING_PARAMS+=("model") -[[ -z "$CAPACITY" ]] && MISSING_PARAMS+=("capacity") - -if [[ ${#MISSING_PARAMS[@]} -ne 0 ]]; then - echo "❌ ERROR: Missing required parameters: ${MISSING_PARAMS[*]}" - echo "Usage: $0 --location --model --capacity [--deployment-type ]" - exit 1 -fi - -if [[ "$DEPLOYMENT_TYPE" != "Standard" && "$DEPLOYMENT_TYPE" != "GlobalStandard" ]]; then - echo "❌ ERROR: Invalid deployment type: $DEPLOYMENT_TYPE. Allowed values are 'Standard' or 'GlobalStandard'." - exit 1 -fi - -MODEL_TYPE="OpenAI.$DEPLOYMENT_TYPE.$MODEL" - -declare -a FALLBACK_REGIONS=() -ROW_NO=1 - -printf "\n%-5s | %-20s | %-40s | %-10s | %-10s | %-10s\n" "No." "Region" "Model Name" "Limit" "Used" "Available" -printf -- "---------------------------------------------------------------------------------------------------------------------\n" - -for region in "${ALL_REGIONS[@]}"; do - MODEL_INFO=$(az cognitiveservices usage list --location "$region" --query "[?name.value=='$MODEL_TYPE']" --output json 2>/dev/null) - - if [[ -n "$MODEL_INFO" && "$MODEL_INFO" != "[]" ]]; then - CURRENT_VALUE=$(echo "$MODEL_INFO" | jq -r '.[0].currentValue // 0' | cut -d'.' -f1) - LIMIT=$(echo "$MODEL_INFO" | jq -r '.[0].limit // 0' | cut -d'.' -f1) - AVAILABLE=$((LIMIT - CURRENT_VALUE)) - - printf "%-5s | %-20s | %-40s | %-10s | %-10s | %-10s\n" "$ROW_NO" "$region" "$MODEL_TYPE" "$LIMIT" "$CURRENT_VALUE" "$AVAILABLE" - - if [[ "$region" == "$LOCATION" && "$AVAILABLE" -ge "$CAPACITY" ]]; then - echo -e "\n✅ Sufficient quota available in user-specified region: $LOCATION" - exit 0 - fi - - if [[ "$region" != "$LOCATION" && "$AVAILABLE" -ge "$CAPACITY" ]]; then - FALLBACK_REGIONS+=("$region ($AVAILABLE)") - fi - fi - - ((ROW_NO++)) -done - -printf -- "---------------------------------------------------------------------------------------------------------------------\n" - -if [[ "${#FALLBACK_REGIONS[@]}" -gt 0 ]]; then - echo -e "\n❌ Deployment cannot proceed because the original region '$LOCATION' lacks sufficient quota." - echo "➡️ You can retry using one of the following regions with sufficient quota:" - for fallback in "${FALLBACK_REGIONS[@]}"; do - echo " • $fallback" - done - echo -e "\n🔧 To proceed, run:" - echo " azd env set AZURE_AISERVICE_LOCATION ''" - echo "📌 To confirm it's set correctly, run:" - echo " azd env get-value AZURE_AISERVICE_LOCATION" - echo "▶️ Once confirmed, re-run azd up to deploy the model in the new region." - exit 2 -fi - -echo "❌ ERROR: No available quota found in any of the fallback regions." -exit 1 \ No newline at end of file From 85b7914e4a0b4421fed2473be415479d40c8ff6a Mon Sep 17 00:00:00 2001 From: Shreyas-Microsoft Date: Tue, 24 Jun 2025 19:03:01 +0530 Subject: [PATCH 3/4] add quota check files --- azure.yaml | 16 ++ scripts/validate_model_deployment_quota.ps1 | 110 ++++++++++++++ scripts/validate_model_deployment_quota.sh | 116 +++++++++++++++ scripts/validate_model_quota.ps1 | 117 +++++++++++++++ scripts/validate_model_quota.sh | 155 ++++++++++++++++++++ 5 files changed, 514 insertions(+) create mode 100644 scripts/validate_model_deployment_quota.ps1 create mode 100644 scripts/validate_model_deployment_quota.sh create mode 100644 scripts/validate_model_quota.ps1 create mode 100644 scripts/validate_model_quota.sh diff --git a/azure.yaml b/azure.yaml index fbf47cd..1171536 100644 --- a/azure.yaml +++ b/azure.yaml @@ -21,3 +21,19 @@ deployment: AzureAiServiceLocation: ${{ parameters.AzureAiServiceLocation }} Prefix: ${{ parameters.Prefix }} baseUrl: ${{ parameters.baseUrl }} +hooks: + preprovision: + posix: + shell: sh + run: > + 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" + interactive: false + continueOnError: false + + windows: + shell: pwsh + run: > + $location = if ($env:AZURE_AISERVICE_LOCATION) { $env:AZURE_AISERVICE_LOCATION } else { "japaneast" }; + ./scripts/validate_model_deployment_quota.ps1 -SubscriptionId $env:AZURE_SUBSCRIPTION_ID -Location $location -ModelsParameter "aiModelDeployments" + interactive: false + continueOnError: false \ No newline at end of file diff --git a/scripts/validate_model_deployment_quota.ps1 b/scripts/validate_model_deployment_quota.ps1 new file mode 100644 index 0000000..57b8396 --- /dev/null +++ b/scripts/validate_model_deployment_quota.ps1 @@ -0,0 +1,110 @@ +param ( + [string]$SubscriptionId, + [string]$Location, + [string]$ModelsParameter +) + +# Read from environment variables (do not pass in azure.yaml) +$AiServiceName = $env:AZURE_AISERVICE_NAME +$ResourceGroup = $env:AZURE_RESOURCE_GROUP + +# Validate required parameters +$MissingParams = @() + +if (-not $SubscriptionId) { + $MissingParams += "subscription" +} +if (-not $Location) { + $MissingParams += "location" +} +if (-not $ModelsParameter) { + $MissingParams += "models-parameter" +} + +if ($MissingParams.Count -gt 0) { + Write-Error "❌ ERROR: Missing required parameters: $($MissingParams -join ', ')" + Write-Host "Usage: .\validate_model_deployment_quotas.ps1 -SubscriptionId -Location -ModelsParameter " + exit 1 +} + +# Load main.parameters.json +$JsonContent = Get-Content -Path "./infra/main.parameters.json" -Raw | ConvertFrom-Json +if (-not $JsonContent) { + Write-Error "❌ ERROR: Failed to parse main.parameters.json. Ensure the JSON file is valid." + exit 1 +} + +$aiModelDeployments = $JsonContent.parameters.$ModelsParameter.value +if (-not $aiModelDeployments -or -not ($aiModelDeployments -is [System.Collections.IEnumerable])) { + Write-Error "❌ ERROR: The specified property '$ModelsParameter' does not exist or is not an array." + exit 1 +} + +# Check if AI resource + all deployments already exist +if ($AiServiceName -and $ResourceGroup) { + $existing = az cognitiveservices account show ` + --name $AiServiceName ` + --resource-group $ResourceGroup ` + --query "name" --output tsv 2>$null + + if ($existing) { + $deployedModels = az cognitiveservices account deployment list ` + --name $AiServiceName ` + --resource-group $ResourceGroup ` + --query "[].name" --output tsv 2>$null + + $requiredDeployments = @() + foreach ($deployment in $aiModelDeployments) { + $requiredDeployments += $deployment.name + } + + $missingDeployments = @() + foreach ($required in $requiredDeployments) { + if ($deployedModels -notcontains $required) { + $missingDeployments += $required + } + } + + if ($missingDeployments.Count -eq 0) { + Write-Host "ℹ️ Azure AI service '$AiServiceName' exists and all required model deployments are provisioned." + Write-Host "⏭️ Skipping quota validation." + exit 0 + } else { + Write-Host "🔍 AI service exists, but the following model deployments are missing: $($missingDeployments -join ', ')" + Write-Host "➡️ Proceeding with quota validation for missing models..." + } + } +} + +# Start quota validation +az account set --subscription $SubscriptionId +Write-Host "🎯 Active Subscription: $(az account show --query '[name, id]' --output tsv)" + +$QuotaAvailable = $true + +foreach ($deployment in $aiModelDeployments) { + $name = if ($env:AZURE_ENV_MODEL_NAME) { $env:AZURE_ENV_MODEL_NAME } else { $deployment.name } + $model = if ($env:AZURE_ENV_MODEL_NAME) { $env:AZURE_ENV_MODEL_NAME } else { $deployment.model.name } + $type = if ($env:AZURE_ENV_MODEL_DEPLOYMENT_TYPE) { $env:AZURE_ENV_MODEL_DEPLOYMENT_TYPE } else { $deployment.sku.name } + $capacity = if ($env:AZURE_ENV_MODEL_CAPACITY) { $env:AZURE_ENV_MODEL_CAPACITY } else { $deployment.sku.capacity } + + Write-Host "`n🔍 Validating model deployment: $name ..." + & .\scripts\validate_model_quota.ps1 -Location $Location -Model $model -Capacity $capacity -DeploymentType $type + $exitCode = $LASTEXITCODE + + if ($exitCode -ne 0) { + if ($exitCode -eq 2) { + exit 1 # already printed, graceful + } + Write-Error "❌ ERROR: Quota validation failed for model deployment: $name" + $QuotaAvailable = $false + } +} + +if (-not $QuotaAvailable) { + Write-Error "❌ ERROR: One or more model deployments failed validation." + exit 1 +} else { + Write-Host "✅ All model deployments passed quota validation successfully." + exit 0 +} \ No newline at end of file diff --git a/scripts/validate_model_deployment_quota.sh b/scripts/validate_model_deployment_quota.sh new file mode 100644 index 0000000..e8018de --- /dev/null +++ b/scripts/validate_model_deployment_quota.sh @@ -0,0 +1,116 @@ +#!/bin/bash + +SUBSCRIPTION_ID="" +LOCATION="" +MODELS_PARAMETER="" + +while [[ $# -gt 0 ]]; do + case "$1" in + --subscription) + SUBSCRIPTION_ID="$2" + shift 2 + ;; + --location) + LOCATION="$2" + shift 2 + ;; + --models-parameter) + MODELS_PARAMETER="$2" + shift 2 + ;; + *) + echo "❌ ERROR: Unknown option: $1" + exit 1 + ;; + esac +done + +# Validate required parameters +MISSING_PARAMS=() +[[ -z "$SUBSCRIPTION_ID" ]] && MISSING_PARAMS+=("subscription") +[[ -z "$LOCATION" ]] && MISSING_PARAMS+=("location") +[[ -z "$MODELS_PARAMETER" ]] && MISSING_PARAMS+=("models-parameter") + +if [[ ${#MISSING_PARAMS[@]} -ne 0 ]]; then + echo "❌ ERROR: Missing required parameters: ${MISSING_PARAMS[*]}" + echo "Usage: $0 --subscription --location --models-parameter " + exit 1 +fi + +# Read from environment +AISERVICE_NAME="${AZURE_AISERVICE_NAME}" +RESOURCE_GROUP="${AZURE_RESOURCE_GROUP}" + +# Check service and deployment existence +if [[ -n "$AISERVICE_NAME" && -n "$RESOURCE_GROUP" ]]; then + existing=$(az cognitiveservices account show --name "$AISERVICE_NAME" --resource-group "$RESOURCE_GROUP" --query "name" --output tsv 2>/dev/null) + if [[ -n "$existing" ]]; then + echo "ℹ️ Found Azure AI service: $AISERVICE_NAME" + + existing_deployments=$(az cognitiveservices account deployment list --name "$AISERVICE_NAME" --resource-group "$RESOURCE_GROUP" --query "[].name" --output tsv 2>/dev/null) + + # Extract required model names + required_models=$(jq -r ".parameters.$MODELS_PARAMETER.value[].name" ./infra/main.parameters.json 2>/dev/null) + + if [[ -z "$required_models" ]]; then + echo "❌ ERROR: Failed to extract required model names from main.parameters.json" + exit 1 + fi + + all_present=true + for model in $required_models; do + if ! grep -q -w "$model" <<< "$existing_deployments"; then + all_present=false + break + fi + done + + if [[ "$all_present" == "true" ]]; then + echo "✅ All required model deployments already exist in AI service '$AISERVICE_NAME'." + echo "⏭️ Skipping quota validation." + exit 0 + else + echo "🔍 AI service exists but some model deployments are missing — proceeding with quota validation." + fi + fi +fi + +# If we reach here, continue with normal quota checks +aiModelDeployments=$(jq -c ".parameters.$MODELS_PARAMETER.value[]" ./infra/main.parameters.json) +if [[ $? -ne 0 ]]; then + echo "❌ ERROR: Failed to parse main.parameters.json. Ensure jq is installed and the JSON is valid." + exit 1 +fi + +az account set --subscription "$SUBSCRIPTION_ID" +echo "🎯 Active Subscription: $(az account show --query '[name, id]' --output tsv)" + +quotaAvailable=true + +while IFS= read -r deployment; do + name=${AZURE_ENV_MODEL_NAME:-$(echo "$deployment" | jq -r '.name')} + model=${AZURE_ENV_MODEL_NAME:-$(echo "$deployment" | jq -r '.model.name')} + type=${AZURE_ENV_MODEL_DEPLOYMENT_TYPE:-$(echo "$deployment" | jq -r '.sku.name')} + capacity=${AZURE_ENV_MODEL_CAPACITY:-$(echo "$deployment" | jq -r '.sku.capacity')} + + echo "🔍 Validating model deployment: $name ..." + ./scripts/validate_model_quota.sh --location "$LOCATION" --model "$model" --capacity "$capacity" --deployment-type "$type" + + exit_code=$? + if [[ $exit_code -ne 0 ]]; then + if [[ $exit_code -eq 2 ]]; then + # Quota validation handled inside script — stop immediately + exit 1 + fi + echo "❌ ERROR: Quota validation failed for model deployment: $name" + quotaAvailable=false + fi +done <<< "$(echo "$aiModelDeployments")" + +if [[ "$quotaAvailable" = false ]]; then + echo "❌ ERROR: One or more model deployments failed validation." + exit 1 +else + echo "✅ All model deployments passed quota validation successfully." + exit 0 +fi \ No newline at end of file diff --git a/scripts/validate_model_quota.ps1 b/scripts/validate_model_quota.ps1 new file mode 100644 index 0000000..0328e1f --- /dev/null +++ b/scripts/validate_model_quota.ps1 @@ -0,0 +1,117 @@ +param ( + [string]$Location, + [string]$Model, + [string]$DeploymentType = "Standard", + [int]$Capacity +) + +# Validate parameters +$MissingParams = @() +if (-not $Location) { $MissingParams += "location" } +if (-not $Model) { $MissingParams += "model" } +if (-not $Capacity) { $MissingParams += "capacity" } +if (-not $DeploymentType) { $MissingParams += "deployment-type" } + +if ($MissingParams.Count -gt 0) { + Write-Error "❌ ERROR: Missing required parameters: $($MissingParams -join ', ')" + Write-Host "Usage: .\validate_model_quota.ps1 -Location -Model -Capacity [-DeploymentType ]" + exit 1 +} + +if ($DeploymentType -ne "Standard" -and $DeploymentType -ne "GlobalStandard") { + Write-Error "❌ ERROR: Invalid deployment type: $DeploymentType. Allowed values are 'Standard' or 'GlobalStandard'." + exit 1 +} + +$ModelType = "OpenAI.$DeploymentType.$Model" +$PreferredRegions = @('australiaeast', 'eastus', 'eastus2', 'francecentral', 'japaneast', 'norwayeast', 'southindia', 'swedencentral', 'uksouth', 'westus', 'westus3') +$AllResults = @() + +function Check-Quota { + param ( + [string]$Region + ) + + try { + $ModelInfoRaw = az cognitiveservices usage list --location $Region --query "[?name.value=='$ModelType']" --output json + $ModelInfo = $ModelInfoRaw | ConvertFrom-Json + if (-not $ModelInfo) { return } + + $CurrentValue = ($ModelInfo | Where-Object { $_.name.value -eq $ModelType }).currentValue + $Limit = ($ModelInfo | Where-Object { $_.name.value -eq $ModelType }).limit + + $CurrentValue = [int]($CurrentValue -replace '\.0+$', '') + $Limit = [int]($Limit -replace '\.0+$', '') + $Available = $Limit - $CurrentValue + + return [PSCustomObject]@{ + Region = $Region + Model = $ModelType + Limit = $Limit + Used = $CurrentValue + Available = $Available + } + } catch { + return + } +} + +# First, check the user-specified region +Write-Host "`n🔍 Checking quota in the requested region '$Location'..." +$PrimaryResult = Check-Quota -Region $Location + +if ($PrimaryResult) { + $AllResults += $PrimaryResult + if ($PrimaryResult.Available -ge $Capacity) { + Write-Host "`n✅ Sufficient quota found in original region '$Location'." + exit 0 + } else { + Write-Host "`n⚠️ Insufficient quota in '$Location' (Available: $($PrimaryResult.Available), Required: $Capacity). Checking fallback regions..." + } +} else { + Write-Host "`n⚠️ Could not retrieve quota info for region '$Location'. Checking fallback regions..." +} + +# Remove primary region from fallback list +$FallbackRegions = $PreferredRegions | Where-Object { $_ -ne $Location } + +foreach ($region in $FallbackRegions) { + $result = Check-Quota -Region $region + if ($result) { + $AllResults += $result + } +} + +# Display Results Table +Write-Host "`n-------------------------------------------------------------------------------------------------------------" +Write-Host "| No. | Region | Model Name | Limit | Used | Available |" +Write-Host "-------------------------------------------------------------------------------------------------------------" + +$count = 1 +foreach ($entry in $AllResults) { + $modelShort = $entry.Model.Substring($entry.Model.LastIndexOf(".") + 1) + Write-Host ("| {0,-4} | {1,-16} | {2,-35} | {3,-7} | {4,-7} | {5,-9} |" -f $count, $entry.Region, $entry.Model, $entry.Limit, $entry.Used, $entry.Available) + $count++ +} +Write-Host "-------------------------------------------------------------------------------------------------------------" + +# Suggest fallback regions +$EligibleFallbacks = $AllResults | Where-Object { $_.Region -ne $Location -and $_.Available -ge $Capacity } + +if ($EligibleFallbacks.Count -gt 0) { + Write-Host "`n❌ Deployment cannot proceed in '$Location'." + Write-Host "➡️ You can retry using one of the following regions with sufficient quota:`n" + foreach ($region in $EligibleFallbacks) { + Write-Host " • $($region.Region) (Available: $($region.Available))" + } + + Write-Host "`n🔧 To proceed, run:" + Write-Host " azd env set AZURE_AISERVICE_LOCATION ''" + Write-Host "📌 To confirm it's set correctly, run:" + Write-Host " azd env get-value AZURE_AISERVICE_LOCATION" + Write-Host "▶️ Once confirmed, re-run azd up to deploy the model in the new region." + exit 2 +} + +Write-Error "`n❌ ERROR: No available quota found in any region." +exit 1 \ No newline at end of file diff --git a/scripts/validate_model_quota.sh b/scripts/validate_model_quota.sh new file mode 100644 index 0000000..c194c53 --- /dev/null +++ b/scripts/validate_model_quota.sh @@ -0,0 +1,155 @@ +#!/bin/bash + +LOCATION="" +MODEL="" +DEPLOYMENT_TYPE="Standard" +CAPACITY=0 + +ALL_REGIONS=('australiaeast' 'eastus' 'eastus2' 'francecentral' 'japaneast' 'norwayeast' 'southindia' 'swedencentral' 'uksouth' 'westus' 'westus3') + +# -------------------- Parse Args -------------------- +while [[ $# -gt 0 ]]; do + case "$1" in + --model) + MODEL="$2" + shift 2 + ;; + --capacity) + CAPACITY="$2" + shift 2 + ;; + --deployment-type) + DEPLOYMENT_TYPE="$2" + shift 2 + ;; + --location) + LOCATION="$2" + shift 2 + ;; + *) + echo "❌ ERROR: Unknown option: $1" + exit 1 + ;; + esac +done + +# -------------------- Validate Inputs -------------------- +MISSING_PARAMS=() +[[ -z "$LOCATION" ]] && MISSING_PARAMS+=("location") +[[ -z "$MODEL" ]] && MISSING_PARAMS+=("model") +[[ "$CAPACITY" -le 0 ]] && MISSING_PARAMS+=("capacity") + +if [[ ${#MISSING_PARAMS[@]} -ne 0 ]]; then + echo "❌ ERROR: Missing or invalid parameters: ${MISSING_PARAMS[*]}" + echo "Usage: $0 --location --model --capacity [--deployment-type ]" + exit 1 +fi + +if [[ "$DEPLOYMENT_TYPE" != "Standard" && "$DEPLOYMENT_TYPE" != "GlobalStandard" ]]; then + echo "❌ ERROR: Invalid deployment type: $DEPLOYMENT_TYPE. Allowed values: 'Standard', 'GlobalStandard'." + exit 1 +fi + +MODEL_TYPE="OpenAI.$DEPLOYMENT_TYPE.$MODEL" +ALL_RESULTS=() +FALLBACK_RESULTS=() +ROW_NO=1 + +# Print validating message only once +echo -e "\n🔍 Validating model deployment: $MODEL ..." + +echo "🔍 Checking quota in the requested region '$LOCATION'..." + +# -------------------- Function: Check Quota -------------------- +check_quota() { + local region="$1" + local output + output=$(az cognitiveservices usage list --location "$region" --query "[?name.value=='$MODEL_TYPE']" --output json 2>/dev/null) + + if [[ -z "$output" || "$output" == "[]" ]]; then + return 2 # No data + fi + + local CURRENT_VALUE + local LIMIT + CURRENT_VALUE=$(echo "$output" | jq -r '.[0].currentValue // 0' | cut -d'.' -f1) + LIMIT=$(echo "$output" | jq -r '.[0].limit // 0' | cut -d'.' -f1) + local AVAILABLE=$((LIMIT - CURRENT_VALUE)) + + ALL_RESULTS+=("$region|$LIMIT|$CURRENT_VALUE|$AVAILABLE") + + if [[ "$AVAILABLE" -ge "$CAPACITY" ]]; then + return 0 + else + return 1 + fi +} + +# -------------------- Check User-Specified Region -------------------- +check_quota "$LOCATION" +primary_status=$? + +if [[ $primary_status -eq 2 ]]; then + echo -e "\n⚠️ Could not retrieve quota info for region: '$LOCATION'." + exit 1 +fi + +if [[ $primary_status -eq 1 ]]; then + # Get available quota from ALL_RESULTS for LOCATION to use in warning + primary_entry="${ALL_RESULTS[0]}" + IFS='|' read -r _ limit used available <<< "$primary_entry" + echo -e "\n⚠️ Insufficient quota in '$LOCATION' (Available: $available, Required: $CAPACITY). Checking fallback regions..." +fi + +# -------------------- Check Fallback Regions -------------------- +for region in "${ALL_REGIONS[@]}"; do + [[ "$region" == "$LOCATION" ]] && continue + check_quota "$region" + if [[ $? -eq 0 ]]; then + FALLBACK_RESULTS+=("$region") + fi +done + +# -------------------- Print Results Table -------------------- +echo "" +printf "%-6s | %-18s | %-35s | %-8s | %-8s | %-9s\n" "No." "Region" "Model Name" "Limit" "Used" "Available" +printf -- "-------------------------------------------------------------------------------------------------------------\n" + +index=1 +for result in "${ALL_RESULTS[@]}"; do + IFS='|' read -r region limit used available <<< "$result" + printf "| %-4s | %-16s | %-33s | %-7s | %-7s | %-9s |\n" "$index" "$region" "$MODEL_TYPE" "$limit" "$used" "$available" + ((index++)) +done +printf -- "-------------------------------------------------------------------------------------------------------------\n" + +# -------------------- Output Result -------------------- +if [[ $primary_status -eq 0 ]]; then + echo -e "\n✅ Sufficient quota found in original region '$LOCATION'." + exit 0 +fi + +if [[ ${#FALLBACK_RESULTS[@]} -gt 0 ]]; then + echo -e "\n❌ Deployment cannot proceed in '$LOCATION'." + echo "➡️ You can retry using one of the following regions with sufficient quota:" + echo "" + for region in "${FALLBACK_RESULTS[@]}"; do + for result in "${ALL_RESULTS[@]}"; do + IFS='|' read -r rgn _ _ avail <<< "$result" + if [[ "$rgn" == "$region" ]]; then + echo " • $region (Available: $avail)" + break + fi + done + done + + echo -e "\n🔧 To proceed, run:" + echo " azd env set AZURE_AISERVICE_LOCATION ''" + echo "📌 To confirm it's set correctly, run:" + echo " azd env get-value AZURE_AISERVICE_LOCATION" + echo "▶️ Once confirmed, re-run azd up to deploy the model in the new region." + exit 2 +fi + +echo -e "\n❌ ERROR: No available quota found in any region." +exit 1 \ No newline at end of file From 1d1aa941103af2227f4cf80929899eaa56523549 Mon Sep 17 00:00:00 2001 From: Shreyas-Microsoft Date: Tue, 24 Jun 2025 19:34:14 +0530 Subject: [PATCH 4/4] add quota check --- scripts/validate_model_deployment_quota.ps1 | 12 +++--------- scripts/validate_model_deployment_quota.sh | 14 +++++++------- 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/scripts/validate_model_deployment_quota.ps1 b/scripts/validate_model_deployment_quota.ps1 index 57b8396..08773d1 100644 --- a/scripts/validate_model_deployment_quota.ps1 +++ b/scripts/validate_model_deployment_quota.ps1 @@ -11,15 +11,9 @@ $ResourceGroup = $env:AZURE_RESOURCE_GROUP # Validate required parameters $MissingParams = @() -if (-not $SubscriptionId) { - $MissingParams += "subscription" -} -if (-not $Location) { - $MissingParams += "location" -} -if (-not $ModelsParameter) { - $MissingParams += "models-parameter" -} +if (-not $SubscriptionId) { $MissingParams += "SubscriptionId" } +if (-not $Location) { $MissingParams += "Location" } +if (-not $ModelsParameter) { $MissingParams += "ModelsParameter" } if ($MissingParams.Count -gt 0) { Write-Error "❌ ERROR: Missing required parameters: $($MissingParams -join ', ')" diff --git a/scripts/validate_model_deployment_quota.sh b/scripts/validate_model_deployment_quota.sh index e8018de..bd098bd 100644 --- a/scripts/validate_model_deployment_quota.sh +++ b/scripts/validate_model_deployment_quota.sh @@ -6,15 +6,15 @@ MODELS_PARAMETER="" while [[ $# -gt 0 ]]; do case "$1" in - --subscription) + --SubscriptionId) SUBSCRIPTION_ID="$2" shift 2 ;; - --location) + --Location) LOCATION="$2" shift 2 ;; - --models-parameter) + --ModelsParameter) MODELS_PARAMETER="$2" shift 2 ;; @@ -27,13 +27,13 @@ done # Validate required parameters MISSING_PARAMS=() -[[ -z "$SUBSCRIPTION_ID" ]] && MISSING_PARAMS+=("subscription") -[[ -z "$LOCATION" ]] && MISSING_PARAMS+=("location") -[[ -z "$MODELS_PARAMETER" ]] && MISSING_PARAMS+=("models-parameter") +[[ -z "$SUBSCRIPTION_ID" ]] && MISSING_PARAMS+=("SubscriptionId") +[[ -z "$LOCATION" ]] && MISSING_PARAMS+=("Location") +[[ -z "$MODELS_PARAMETER" ]] && MISSING_PARAMS+=("ModelsParameter") if [[ ${#MISSING_PARAMS[@]} -ne 0 ]]; then echo "❌ ERROR: Missing required parameters: ${MISSING_PARAMS[*]}" - echo "Usage: $0 --subscription --location --models-parameter " + echo "Usage: $0 --SubscriptionId --Location --ModelsParameter " exit 1 fi