From 6f58c237f0af9cb98333d832136d09b35ada5ded Mon Sep 17 00:00:00 2001 From: Priyanka-Microsoft Date: Thu, 5 Jun 2025 14:06:50 +0530 Subject: [PATCH 1/4] quota auto validation before deployment --- azure.yaml | 18 ++- infra/main.parameters.json | 75 ++++++++++++ .../validate_model_deployment_quota.sh | 88 ++++++++++++++ .../validate_model_deployment_quotas.ps1 | 69 +++++++++++ infra/scripts/validate_model_quota.ps1 | 108 ++++++++++++++++++ infra/scripts/validate_model_quota.sh | 100 ++++++++++++++++ 6 files changed, 457 insertions(+), 1 deletion(-) create mode 100644 infra/main.parameters.json create mode 100644 infra/scripts/validate_model_deployment_quota.sh create mode 100644 infra/scripts/validate_model_deployment_quotas.ps1 create mode 100644 infra/scripts/validate_model_quota.ps1 create mode 100644 infra/scripts/validate_model_quota.sh diff --git a/azure.yaml b/azure.yaml index ee4810b1..d7df9703 100644 --- a/azure.yaml +++ b/azure.yaml @@ -1,4 +1,20 @@ # yaml-language-server: $schema=https://raw.githubusercontent.com/Azure/azure-dev/main/schemas/v1.0/azure.yaml.json name: multi-agent-custom-automation-engine-solution-accelerator metadata: - template: multi-agent-custom-automation-engine-solution-accelerator@1.0 \ No newline at end of file + template: multi-agent-custom-automation-engine-solution-accelerator@1.0 +hooks: + preprovision: + posix: + shell: sh + run: > + ./infra/scripts/validate_model_deployment_quota.sh --subscription "$AZURE_SUBSCRIPTION_ID" --location "${AZURE_OPENAI_LOCATION:-swedencentral}" --models-parameter "aiModelDeployments" + interactive: false + continueOnError: false + + windows: + shell: pwsh + run: > + $location = if ($env:AZURE_OPENAI_LOCATION) { $env:AZURE_OPENAI_LOCATION } else { "swedencentral" }; + ./infra/scripts/validate_model_deployment_quotas.ps1 -SubscriptionId $env:AZURE_SUBSCRIPTION_ID -Location $location -ModelsParameter "aiModelDeployments" + interactive: false + continueOnError: false \ No newline at end of file diff --git a/infra/main.parameters.json b/infra/main.parameters.json new file mode 100644 index 00000000..d93f0064 --- /dev/null +++ b/infra/main.parameters.json @@ -0,0 +1,75 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "aiModelDeployments": { + "value": [ + { + "name": "gpt", + "model": { + "name": "gpt-4o", + "version": "2024-08-06", + "format": "OpenAI" + }, + "sku": { + "name": "GlobalStandard", + "capacity": 140 + } + } + ] + }, + "environmentName": { + "value": "${AZURE_ENV_NAME}" + }, + "location": { + "value": "${AZURE_LOCATION}" + }, + "backendExists": { + "value": "${SERVICE_BACKEND_RESOURCE_EXISTS=false}" + }, + "backendDefinition": { + "value": { + "settings": [ + { + "name": "", + "value": "${VAR}", + "_comment_name": "The name of the environment variable when running in Azure. If empty, ignored.", + "_comment_value": "The value to provide. This can be a fixed literal, or an expression like ${VAR} to use the value of 'VAR' from the current environment." + }, + { + "name": "", + "value": "${VAR_S}", + "secret": true, + "_comment_name": "The name of the environment variable when running in Azure. If empty, ignored.", + "_comment_value": "The value to provide. This can be a fixed literal, or an expression like ${VAR_S} to use the value of 'VAR_S' from the current environment." + } + ] + } + }, + "frontendExists": { + "value": "${SERVICE_FRONTEND_RESOURCE_EXISTS=false}" + }, + "frontendDefinition": { + "value": { + "settings": [ + { + "name": "", + "value": "${VAR}", + "_comment_name": "The name of the environment variable when running in Azure. If empty, ignored.", + "_comment_value": "The value to provide. This can be a fixed literal, or an expression like ${VAR} to use the value of 'VAR' from the current environment." + }, + { + "name": "", + "value": "${VAR_S}", + "secret": true, + "_comment_name": "The name of the environment variable when running in Azure. If empty, ignored.", + "_comment_value": "The value to provide. This can be a fixed literal, or an expression like ${VAR_S} to use the value of 'VAR_S' from the current environment." + } + ] + } + }, + "principalId": { + "value": "${AZURE_PRINCIPAL_ID}" + } + } +} \ No newline at end of file diff --git a/infra/scripts/validate_model_deployment_quota.sh b/infra/scripts/validate_model_deployment_quota.sh new file mode 100644 index 00000000..1f890b0e --- /dev/null +++ b/infra/scripts/validate_model_deployment_quota.sh @@ -0,0 +1,88 @@ +#!/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=$(echo "$deployment" | jq -r '.name') + model=$(echo "$deployment" | jq -r '.model.name') + type=$(echo "$deployment" | jq -r '.sku.name') + capacity=$(echo "$deployment" | jq -r '.sku.capacity') + + echo "šŸ” Validating model deployment: $name ..." + ./infra/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/infra/scripts/validate_model_deployment_quotas.ps1 b/infra/scripts/validate_model_deployment_quotas.ps1 new file mode 100644 index 00000000..dfe5f7ef --- /dev/null +++ b/infra/scripts/validate_model_deployment_quotas.ps1 @@ -0,0 +1,69 @@ +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 = $deployment.name + $model = $deployment.model.name + $type = $deployment.sku.name + $capacity = $deployment.sku.capacity + + Write-Host "šŸ” Validating model deployment: $name ..." + & .\infra\scripts\validate_model_quota.ps1 -Location $Location -Model $model -Capacity $capacity -DeploymentType $type + + # Check if the script failed + if ($LASTEXITCODE -ne 0) { + 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/infra/scripts/validate_model_quota.ps1 b/infra/scripts/validate_model_quota.ps1 new file mode 100644 index 00000000..fb3bbbb1 --- /dev/null +++ b/infra/scripts/validate_model_quota.ps1 @@ -0,0 +1,108 @@ +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', 'eastus2', 'francecentral', 'japaneast', 'norwayeast', 'swedencentral', 'uksouth', 'westus') +$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_OPENAI_LOCATION ''" + Write-Host "šŸ“Œ To confirm it's set correctly, run:" + Write-Host " azd env get-value AZURE_OPENAI_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/infra/scripts/validate_model_quota.sh b/infra/scripts/validate_model_quota.sh new file mode 100644 index 00000000..eb8d6b25 --- /dev/null +++ b/infra/scripts/validate_model_quota.sh @@ -0,0 +1,100 @@ +#!/bin/bash + +LOCATION="" +MODEL="" +DEPLOYMENT_TYPE="Standard" +CAPACITY=0 + +ALL_REGIONS=('australiaeast' 'eastus2' 'francecentral' 'japaneast' 'norwayeast' 'swedencentral' 'uksouth' 'westus') + +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_OPENAI_LOCATION ''" + echo "šŸ“Œ To confirm it's set correctly, run:" + echo " azd env get-value AZURE_OPENAI_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 From d50cc7b1ef68892ae1a0058a3f13cc55cc2372fc Mon Sep 17 00:00:00 2001 From: Priyanka-Microsoft Date: Thu, 5 Jun 2025 14:36:38 +0530 Subject: [PATCH 2/4] updated exit code condition --- infra/scripts/validate_model_deployment_quotas.ps1 | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/infra/scripts/validate_model_deployment_quotas.ps1 b/infra/scripts/validate_model_deployment_quotas.ps1 index dfe5f7ef..94bc08a0 100644 --- a/infra/scripts/validate_model_deployment_quotas.ps1 +++ b/infra/scripts/validate_model_deployment_quotas.ps1 @@ -54,7 +54,13 @@ foreach ($deployment in $aiModelDeployments) { & .\infra\scripts\validate_model_quota.ps1 -Location $Location -Model $model -Capacity $capacity -DeploymentType $type # Check if the script failed - if ($LASTEXITCODE -ne 0) { + $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 } From a484e5a3c4819a992057827aa3eca9a99306fb11 Mon Sep 17 00:00:00 2001 From: Priyanka-Microsoft Date: Thu, 5 Jun 2025 14:47:50 +0530 Subject: [PATCH 3/4] changed the variable name --- azure.yaml | 4 ++-- infra/scripts/validate_model_quota.ps1 | 4 ++-- infra/scripts/validate_model_quota.sh | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/azure.yaml b/azure.yaml index d7df9703..6193e3d5 100644 --- a/azure.yaml +++ b/azure.yaml @@ -7,14 +7,14 @@ hooks: posix: shell: sh run: > - ./infra/scripts/validate_model_deployment_quota.sh --subscription "$AZURE_SUBSCRIPTION_ID" --location "${AZURE_OPENAI_LOCATION:-swedencentral}" --models-parameter "aiModelDeployments" + ./infra/scripts/validate_model_deployment_quota.sh --subscription "$AZURE_SUBSCRIPTION_ID" --location "${AZURE_ENV_OPENAI_LOCATION:-swedencentral}" --models-parameter "aiModelDeployments" interactive: false continueOnError: false windows: shell: pwsh run: > - $location = if ($env:AZURE_OPENAI_LOCATION) { $env:AZURE_OPENAI_LOCATION } else { "swedencentral" }; + $location = if ($env:AZURE_ENV_OPENAI_LOCATION) { $env:AZURE_ENV_OPENAI_LOCATION } else { "swedencentral" }; ./infra/scripts/validate_model_deployment_quotas.ps1 -SubscriptionId $env:AZURE_SUBSCRIPTION_ID -Location $location -ModelsParameter "aiModelDeployments" interactive: false continueOnError: false \ No newline at end of file diff --git a/infra/scripts/validate_model_quota.ps1 b/infra/scripts/validate_model_quota.ps1 index fb3bbbb1..fc217b99 100644 --- a/infra/scripts/validate_model_quota.ps1 +++ b/infra/scripts/validate_model_quota.ps1 @@ -97,9 +97,9 @@ if ($FallbackRegions.Count -gt 0) { } Write-Host "`nšŸ”§ To proceed, run:" - Write-Host " azd env set AZURE_OPENAI_LOCATION ''" + Write-Host " azd env set AZURE_ENV_OPENAI_LOCATION ''" Write-Host "šŸ“Œ To confirm it's set correctly, run:" - Write-Host " azd env get-value AZURE_OPENAI_LOCATION" + Write-Host " azd env get-value AZURE_ENV_OPENAI_LOCATION" Write-Host "ā–¶ļø Once confirmed, re-run azd up to deploy the model in the new region." exit 2 } diff --git a/infra/scripts/validate_model_quota.sh b/infra/scripts/validate_model_quota.sh index eb8d6b25..ae56ae0f 100644 --- a/infra/scripts/validate_model_quota.sh +++ b/infra/scripts/validate_model_quota.sh @@ -89,9 +89,9 @@ if [[ "${#FALLBACK_REGIONS[@]}" -gt 0 ]]; then echo " • $fallback" done echo -e "\nšŸ”§ To proceed, run:" - echo " azd env set AZURE_OPENAI_LOCATION ''" + echo " azd env set AZURE_ENV_OPENAI_LOCATION ''" echo "šŸ“Œ To confirm it's set correctly, run:" - echo " azd env get-value AZURE_OPENAI_LOCATION" + echo " azd env get-value AZURE_ENV_OPENAI_LOCATION" echo "ā–¶ļø Once confirmed, re-run azd up to deploy the model in the new region." exit 2 fi From ea8101d1aea0a6de54e0d24a9885b4413d5c162c Mon Sep 17 00:00:00 2001 From: Priyanka-Microsoft Date: Thu, 5 Jun 2025 15:04:17 +0530 Subject: [PATCH 4/4] updated region --- infra/scripts/quota_check_params.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/infra/scripts/quota_check_params.sh b/infra/scripts/quota_check_params.sh index add6ac47..71df64e0 100644 --- a/infra/scripts/quota_check_params.sh +++ b/infra/scripts/quota_check_params.sh @@ -92,7 +92,7 @@ az account set --subscription "$AZURE_SUBSCRIPTION_ID" echo "šŸŽÆ Active Subscription: $(az account show --query '[name, id]' --output tsv)" # Default Regions to check (Comma-separated, now configurable) -DEFAULT_REGIONS="eastus,uksouth,eastus2,northcentralus,swedencentral,westus,westus2,southcentralus,canadacentral" +DEFAULT_REGIONS="australiaeast,eastus2,francecentral,japaneast,norwayeast,swedencentral,uksouth,westus" IFS=',' read -r -a DEFAULT_REGION_ARRAY <<< "$DEFAULT_REGIONS" # Read parameters (if any)