diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile
new file mode 100644
index 000000000..20757f991
--- /dev/null
+++ b/.devcontainer/Dockerfile
@@ -0,0 +1,4 @@
+FROM mcr.microsoft.com/devcontainers/python:1-3.11-bullseye
+
+# Remove Yarn repository to avoid GPG key expiration issue
+RUN rm -f /etc/apt/sources.list.d/yarn.list
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index 0cc98ccfd..2fdecaef9 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -3,7 +3,9 @@
{
"name": "Python 3",
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
- "image": "mcr.microsoft.com/devcontainers/python:1-3.11-bullseye",
+ "build": {
+ "dockerfile": "Dockerfile"
+ },
"features": {
"ghcr.io/devcontainers/features/azure-cli:1": {
"installBicep": true,
@@ -37,4 +39,4 @@
"hostRequirements": {
"memory": "4gb"
}
-}
\ No newline at end of file
+}
diff --git a/.github/workflows/CAdeploy.yml b/.github/workflows/CAdeploy.yml
index 0b27423e9..758c11047 100644
--- a/.github/workflows/CAdeploy.yml
+++ b/.github/workflows/CAdeploy.yml
@@ -7,7 +7,17 @@ on:
- dev
- demo
schedule:
- - cron: "0 6,18 * * *" # Runs at 6:00 AM and 6:00 PM GMT
+ - cron: "0 6 * * *" # Runs at 6:00 AM UTC daily
+ workflow_dispatch:
+ inputs:
+ DEPLOYMENT_TYPE:
+ description: 'Specify deployment type: WAF or Non-WAF'
+ required: false
+ type: choice
+ options:
+ - WAF
+ - Non-WAF
+ default: 'Non-WAF'
env:
GPT_MIN_CAPACITY: 200
@@ -21,10 +31,9 @@ jobs:
RESOURCE_GROUP_NAME: ${{ steps.check_create_rg.outputs.RESOURCE_GROUP_NAME }}
WEBAPP_URL: ${{ steps.get_output.outputs.WEBAPP_URL }}
DEPLOYMENT_SUCCESS: ${{ steps.deployment_status.outputs.SUCCESS }}
- AI_SERVICES_NAME: ${{ steps.get_ai_services_name.outputs.AI_SERVICES_NAME }}
- KEYVAULTS: ${{ steps.list_keyvaults.outputs.KEYVAULTS }}
AZURE_LOCATION: ${{ steps.set_region.outputs.AZURE_LOCATION }}
- SOLUTION_PREFIX: ${{ steps.generate_solution_prefix.outputs.SOLUTION_PREFIX }}
+ ENV_NAME: ${{ steps.generate_env_name.outputs.ENV_NAME }}
+ DEPLOYMENT_TYPE: ${{ steps.determine_deployment_type.outputs.DEPLOYMENT_TYPE }}
steps:
- name: Checkout
@@ -38,12 +47,23 @@ jobs:
sudo apt-get install -y unixodbc-dev
- name: Setup Azure CLI
- run: curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash
+ run: |
+ curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash
+ az --version
+
+ - name: Setup Azure Developer CLI
+ run: |
+ curl -fsSL https://aka.ms/install-azd.sh | sudo bash
+ azd version
- name: Login to Azure
run: |
+ echo "Logging into Azure..."
az login --service-principal -u ${{ secrets.AZURE_CLIENT_ID }} -p ${{ secrets.AZURE_CLIENT_SECRET }} --tenant ${{ secrets.AZURE_TENANT_ID }}
+ azd auth login --client-id ${{ secrets.AZURE_CLIENT_ID }} --client-secret ${{ secrets.AZURE_CLIENT_SECRET }} --tenant-id ${{ secrets.AZURE_TENANT_ID }}
+ echo "Setting default subscription..."
az account set --subscription ${{ secrets.AZURE_SUBSCRIPTION_ID }}
+ azd config set defaults.subscription ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- name: Run Quota Check
id: quota-check
@@ -96,6 +116,7 @@ jobs:
UNIQUE_RG_NAME="arg-${ACCL_NAME}-${SHORT_UUID}"
echo "RESOURCE_GROUP_NAME=${UNIQUE_RG_NAME}" >> $GITHUB_ENV
echo "Generated RESOURCE_GROUP_NAME: ${UNIQUE_RG_NAME}"
+
- name: Check and Create Resource Group
id: check_create_rg
run: |
@@ -111,17 +132,18 @@ jobs:
fi
# Set output for other jobs
echo "RESOURCE_GROUP_NAME=${{ env.RESOURCE_GROUP_NAME }}" >> $GITHUB_OUTPUT
- - name: Generate Unique Solution Prefix
- id: generate_solution_prefix
+
+ - name: Generate Unique Environment Name
+ id: generate_env_name
run: |
set -e
COMMON_PART="pslc"
TIMESTAMP=$(date +%s)
UPDATED_TIMESTAMP=$(echo $TIMESTAMP | tail -c 3)
- UNIQUE_SOLUTION_PREFIX="${COMMON_PART}${UPDATED_TIMESTAMP}"
- echo "SOLUTION_PREFIX=${UNIQUE_SOLUTION_PREFIX}" >> $GITHUB_ENV
- echo "SOLUTION_PREFIX=${UNIQUE_SOLUTION_PREFIX}" >> $GITHUB_OUTPUT
- echo "Generated SOLUTION_PREFIX: ${UNIQUE_SOLUTION_PREFIX}"
+ UNIQUE_ENV_NAME="${COMMON_PART}${UPDATED_TIMESTAMP}"
+ echo "ENV_NAME=${UNIQUE_ENV_NAME}" >> $GITHUB_ENV
+ echo "ENV_NAME=${UNIQUE_ENV_NAME}" >> $GITHUB_OUTPUT
+ echo "Generated ENV_NAME: ${UNIQUE_ENV_NAME}"
- name: Determine Tag
id: determine_tag
@@ -133,63 +155,262 @@ jobs:
else TAG="latest_waf"; fi
echo "IMAGE_TAG=$TAG" >> $GITHUB_ENV
echo "Image Tag: $TAG"
- - name: Deploy and extract values from deployment output
- id: get_output
+
+ - name: Determine Deployment Type
+ id: determine_deployment_type
run: |
set -e
- echo "Fetching deployment output..."
- # Install azd (Azure Developer CLI) - required by process_sample_data.sh
- curl -fsSL https://aka.ms/install-azd.sh | bash
-
- # Generate current timestamp in desired format: YYYY-MM-DDTHH:MM:SS.SSSSSSSZ
- current_date=$(date -u +"%Y-%m-%dT%H:%M:%S.%7NZ")
+ # Get day of month (1-31)
+ DAY=$(date +%d)
+ # Remove leading zero
+ DAY=$((10#$DAY))
- DEPLOY_OUTPUT=$(az deployment group create \
- --resource-group ${{ env.RESOURCE_GROUP_NAME }} \
- --template-file infra/main.bicep \
- --parameters location=${{ env.AZURE_LOCATION }} azureAiServiceLocation=${{ env.AZURE_LOCATION }} solutionName=${{ env.SOLUTION_PREFIX }} cosmosLocation=westus gptModelCapacity=${{ env.GPT_MIN_CAPACITY }} embeddingDeploymentCapacity=${{ env.TEXT_EMBEDDING_MIN_CAPACITY }} imageTag=${{ env.IMAGE_TAG }} createdBy="Pipeline" tags="{'SecurityControl':'Ignore','Purpose':'Deploying and Cleaning Up Resources for Validation','CreatedDate':'$current_date'}" \
- --query "properties.outputs" -o json)
-
- echo "Deployment output: $DEPLOY_OUTPUT"
- if [[ -z "$DEPLOY_OUTPUT" ]]; then
- echo "Error: Deployment output is empty. Please check the deployment logs."
- exit 1
+ # Check if deployment type is provided via workflow dispatch
+ if [ "${{ github.event.inputs.DEPLOYMENT_TYPE }}" != "" ]; then
+ DEPLOYMENT_TYPE="${{ github.event.inputs.DEPLOYMENT_TYPE }}"
+ echo "DEPLOYMENT_TYPE=$DEPLOYMENT_TYPE" >> $GITHUB_OUTPUT
+ echo "DEPLOYMENT_TYPE=$DEPLOYMENT_TYPE" >> $GITHUB_ENV
+ echo "Deployment type provided via workflow dispatch: $DEPLOYMENT_TYPE"
+ exit 0
+ fi
+
+ # Alternate based on odd/even day
+ if [ $((DAY % 2)) -eq 0 ]; then
+ echo "DEPLOYMENT_TYPE=WAF" >> $GITHUB_OUTPUT
+ echo "DEPLOYMENT_TYPE=WAF" >> $GITHUB_ENV
+ echo "Even day detected ($DAY). Setting deployment type to WAF."
+ else
+ echo "DEPLOYMENT_TYPE=Non-WAF" >> $GITHUB_OUTPUT
+ echo "DEPLOYMENT_TYPE=Non-WAF" >> $GITHUB_ENV
+ echo "Odd day detected ($DAY). Setting deployment type to Non-WAF."
fi
- export AI_FOUNDRY_RESOURCE_ID=$(echo "$DEPLOY_OUTPUT" | jq -r '.aI_FOUNDRY_RESOURCE_ID.value')
+ - name: Copy Parameters File Based on Deployment Type
+ id: copy_parameters_file
+ run: |
+ set -e
+ if [ "${{ env.DEPLOYMENT_TYPE }}" == "WAF" ]; then
+ echo "Using WAF parameters file."
+ cp -f infra/main.waf.parameters.json infra/main.parameters.json
+ else
+ echo "Using Non-WAF parameters file."
+ fi
+
+ - name: Create azd environment
+ id: create_azd_env
+ run: |
+ set -e
+ echo "Creating azd environment..."
+ azd env new ${{ env.ENV_NAME }} --no-prompt
+ echo "Environment created: ${{ env.ENV_NAME }}"
+
+ echo "Setting environment variables..."
+ azd env set AZURE_RESOURCE_GROUP="${{ env.RESOURCE_GROUP_NAME }}"
+ azd env set AZURE_SUBSCRIPTION_ID="${{ secrets.AZURE_SUBSCRIPTION_ID }}"
+ azd env set AZURE_LOCATION="${{ env.AZURE_LOCATION }}"
+ azd env set AZURE_ENV_AI_SERVICE_LOCATION="${{ env.AZURE_LOCATION }}"
+ azd env set AZURE_ENV_MODEL_CAPACITY="${{ env.GPT_MIN_CAPACITY }}"
+ azd env set AZURE_ENV_EMBEDDING_MODEL_CAPACITY="${{ env.TEXT_EMBEDDING_MIN_CAPACITY }}"
+ azd env set AZURE_ENV_IMAGETAG="${IMAGE_TAG}"
+
+ echo "Environment variables set successfully:"
+ azd env get-values
+
+ - name: Deploy using azd
+ id: deploy
+ run: |
+ set -e
+ azd up --no-prompt
+ echo "Deployment completed successfully."
+
+ - name: Extract values from deployment output
+ id: get_output
+ run: |
+ set -e
+ echo "Fetching deployment output..."
+
+ AI_FOUNDRY_RESOURCE_ID=$(azd env get-value AI_FOUNDRY_RESOURCE_ID)
echo "AI_FOUNDRY_RESOURCE_ID=$AI_FOUNDRY_RESOURCE_ID" >> $GITHUB_ENV
- export SEARCH_SERVICE_NAME=$(echo "$DEPLOY_OUTPUT" | jq -r '.aI_SEARCH_SERVICE_NAME.value')
+ echo "Deployed AI_FOUNDRY_RESOURCE_ID: $AI_FOUNDRY_RESOURCE_ID"
+
+ SEARCH_SERVICE_NAME=$(azd env get-value AI_SEARCH_SERVICE_NAME)
echo "SEARCH_SERVICE_NAME=$SEARCH_SERVICE_NAME" >> $GITHUB_ENV
- export COSMOS_DB_ACCOUNT_NAME=$(echo "$DEPLOY_OUTPUT" | jq -r '.cosmosdB_ACCOUNT_NAME.value')
+ echo "Deployed SEARCH_SERVICE_NAME: $SEARCH_SERVICE_NAME"
+
+ COSMOS_DB_ACCOUNT_NAME=$(azd env get-value COSMOSDB_ACCOUNT_NAME)
echo "COSMOS_DB_ACCOUNT_NAME=$COSMOS_DB_ACCOUNT_NAME" >> $GITHUB_ENV
- export STORAGE_ACCOUNT=$(echo "$DEPLOY_OUTPUT" | jq -r '.storagE_ACCOUNT_NAME.value')
+ echo "Deployed COSMOS_DB_ACCOUNT_NAME: $COSMOS_DB_ACCOUNT_NAME"
+
+ STORAGE_ACCOUNT=$(azd env get-value STORAGE_ACCOUNT_NAME)
echo "STORAGE_ACCOUNT=$STORAGE_ACCOUNT" >> $GITHUB_ENV
- export STORAGE_CONTAINER=$(echo "$DEPLOY_OUTPUT" | jq -r '.storagE_CONTAINER_NAME.value')
+ echo "Deployed STORAGE_ACCOUNT: $STORAGE_ACCOUNT"
+
+ STORAGE_CONTAINER=$(azd env get-value STORAGE_CONTAINER_NAME)
echo "STORAGE_CONTAINER=$STORAGE_CONTAINER" >> $GITHUB_ENV
- export KEYVAULT_NAME=$(echo "$DEPLOY_OUTPUT" | jq -r '.keY_VAULT_NAME.value')
+ echo "Deployed STORAGE_CONTAINER: $STORAGE_CONTAINER"
+
+ KEYVAULT_NAME=$(azd env get-value KEY_VAULT_NAME)
echo "KEYVAULT_NAME=$KEYVAULT_NAME" >> $GITHUB_ENV
- export SQL_SERVER_NAME=$(echo "$DEPLOY_OUTPUT" | jq -r '.sqldB_SERVER_NAME.value')
+ echo "Deployed KEYVAULT_NAME: $KEYVAULT_NAME"
+
+ SQL_SERVER_NAME=$(azd env get-value SQLDB_SERVER_NAME)
echo "SQL_SERVER_NAME=$SQL_SERVER_NAME" >> $GITHUB_ENV
- export SQL_DATABASE=$(echo "$DEPLOY_OUTPUT" | jq -r '.sqldB_DATABASE.value')
+ echo "Deployed SQL_SERVER_NAME: $SQL_SERVER_NAME"
+
+ SQL_DATABASE=$(azd env get-value SQLDB_DATABASE)
echo "SQL_DATABASE=$SQL_DATABASE" >> $GITHUB_ENV
- export CLIENT_ID=$(echo "$DEPLOY_OUTPUT" | jq -r '.managedidentitY_SQL_CLIENTID.value')
+ echo "Deployed SQL_DATABASE: $SQL_DATABASE"
+
+ CLIENT_ID=$(azd env get-value MANAGEDIDENTITY_SQL_CLIENTID)
echo "CLIENT_ID=$CLIENT_ID" >> $GITHUB_ENV
- export CLIENT_NAME=$(echo "$DEPLOY_OUTPUT" | jq -r '.managedidentitY_SQL_NAME.value')
+ echo "Deployed MANAGED IDENTITY CLIENT_ID: $CLIENT_ID"
+
+ CLIENT_NAME=$(azd env get-value MANAGEDIDENTITY_SQL_NAME)
echo "CLIENT_NAME=$CLIENT_NAME" >> $GITHUB_ENV
- export RG_NAME=$(echo "$DEPLOY_OUTPUT" | jq -r '.resourcE_GROUP_NAME.value')
+ echo "Deployed MANAGED IDENTITY CLIENT_NAME: $CLIENT_NAME"
+
+ RG_NAME=$(azd env get-value RESOURCE_GROUP_NAME)
echo "RG_NAME=$RG_NAME" >> $GITHUB_ENV
- WEBAPP_URL=$(echo $DEPLOY_OUTPUT | jq -r '.weB_APP_URL.value')
+
+ WEBAPP_URL=$(azd env get-value WEB_APP_URL)
echo "WEBAPP_URL=$WEBAPP_URL" >> $GITHUB_OUTPUT
- WEB_APP_NAME=$(echo $DEPLOY_OUTPUT | jq -r '.weB_APP_NAME.value')
+ echo "Deployed WEB APP URL: $WEBAPP_URL"
+
+ WEB_APP_NAME=$(azd env get-value WEB_APP_NAME)
echo "WEB_APP_NAME=$WEB_APP_NAME" >> $GITHUB_ENV
- echo "Deployment output: $DEPLOY_OUTPUT"
echo "🔧 Disabling AUTH_ENABLED for the web app..."
az webapp config appsettings set -g "$RG_NAME" -n "$WEB_APP_NAME" --settings AUTH_ENABLED=false
sleep 30
- - name: Deploy Infra and Import Sample Data
+ - name: Enable Public Network Access (WAF Only)
+ if: env.DEPLOYMENT_TYPE == 'WAF'
+ run: |
+ set -e
+ echo "=== Temporarily enabling public network access for WAF deployment ==="
+
+ # Enable public access for Storage Account
+ echo "Enabling public access for Storage Account: ${{ env.STORAGE_ACCOUNT }}"
+ ORIGINAL_STORAGE_PUBLIC_ACCESS=$(az storage account show \
+ --name "${{ env.STORAGE_ACCOUNT }}" \
+ --resource-group "${{ env.RG_NAME }}" \
+ --query "publicNetworkAccess" \
+ -o tsv)
+ echo "ORIGINAL_STORAGE_PUBLIC_ACCESS=$ORIGINAL_STORAGE_PUBLIC_ACCESS" >> $GITHUB_ENV
+
+ ORIGINAL_STORAGE_DEFAULT_ACTION=$(az storage account show \
+ --name "${{ env.STORAGE_ACCOUNT }}" \
+ --resource-group "${{ env.RG_NAME }}" \
+ --query "networkRuleSet.defaultAction" \
+ -o tsv)
+ echo "ORIGINAL_STORAGE_DEFAULT_ACTION=$ORIGINAL_STORAGE_DEFAULT_ACTION" >> $GITHUB_ENV
+
+ if [ "$ORIGINAL_STORAGE_PUBLIC_ACCESS" != "Enabled" ]; then
+ az storage account update \
+ --name "${{ env.STORAGE_ACCOUNT }}" \
+ --resource-group "${{ env.RG_NAME }}" \
+ --public-network-access Enabled \
+ --output none
+ echo "✓ Storage Account public access enabled"
+ fi
+
+ if [ "$ORIGINAL_STORAGE_DEFAULT_ACTION" != "Allow" ]; then
+ az storage account update \
+ --name "${{ env.STORAGE_ACCOUNT }}" \
+ --resource-group "${{ env.RG_NAME }}" \
+ --default-action Allow \
+ --output none
+ echo "✓ Storage Account network default action set to Allow"
+ fi
+
+ # Enable public access for AI Foundry
+ AIF_ACCOUNT_RESOURCE_ID=$(echo "${{ env.AI_FOUNDRY_RESOURCE_ID }}" | sed 's|/projects/.*||')
+ AIF_RESOURCE_NAME=$(basename "$AIF_ACCOUNT_RESOURCE_ID")
+ AIF_RESOURCE_GROUP=$(echo "$AIF_ACCOUNT_RESOURCE_ID" | sed -n 's|.*/resourceGroups/\([^/]*\)/.*|\1|p')
+ AIF_SUBSCRIPTION_ID=$(echo "$AIF_ACCOUNT_RESOURCE_ID" | sed -n 's|.*/subscriptions/\([^/]*\)/.*|\1|p')
+
+ echo "AIF_ACCOUNT_RESOURCE_ID=$AIF_ACCOUNT_RESOURCE_ID" >> $GITHUB_ENV
+
+ ORIGINAL_FOUNDRY_PUBLIC_ACCESS=$(az cognitiveservices account show \
+ --name "$AIF_RESOURCE_NAME" \
+ --resource-group "$AIF_RESOURCE_GROUP" \
+ --subscription "$AIF_SUBSCRIPTION_ID" \
+ --query "properties.publicNetworkAccess" \
+ --output tsv || echo "")
+ echo "ORIGINAL_FOUNDRY_PUBLIC_ACCESS=$ORIGINAL_FOUNDRY_PUBLIC_ACCESS" >> $GITHUB_ENV
+
+ if [ -n "$ORIGINAL_FOUNDRY_PUBLIC_ACCESS" ] && [ "$ORIGINAL_FOUNDRY_PUBLIC_ACCESS" != "Enabled" ]; then
+ az resource update \
+ --ids "$AIF_ACCOUNT_RESOURCE_ID" \
+ --api-version 2024-10-01 \
+ --set properties.publicNetworkAccess=Enabled properties.apiProperties="{}" \
+ --output none || echo "⚠ Warning: Failed to enable AI Foundry public access"
+ echo "✓ AI Foundry public access enabled"
+ fi
+
+ # Enable public access for Key Vault
+ echo "Enabling public access for Key Vault: ${{ env.KEYVAULT_NAME }}"
+ ORIGINAL_KEYVAULT_PUBLIC_ACCESS=$(az keyvault show \
+ --name "${{ env.KEYVAULT_NAME }}" \
+ --resource-group "${{ env.RG_NAME }}" \
+ --query "properties.publicNetworkAccess" \
+ -o tsv)
+ echo "ORIGINAL_KEYVAULT_PUBLIC_ACCESS=$ORIGINAL_KEYVAULT_PUBLIC_ACCESS" >> $GITHUB_ENV
+
+ if [ "$ORIGINAL_KEYVAULT_PUBLIC_ACCESS" != "Enabled" ]; then
+ az keyvault update \
+ --name "${{ env.KEYVAULT_NAME }}" \
+ --resource-group "${{ env.RG_NAME }}" \
+ --public-network-access Enabled \
+ --output none
+ echo "✓ Key Vault public access enabled"
+ fi
+
+ # Enable public access for SQL Server
+ echo "Enabling public access for SQL Server: ${{ env.SQL_SERVER_NAME }}"
+ ORIGINAL_SQL_PUBLIC_ACCESS=$(az sql server show \
+ --name "${{ env.SQL_SERVER_NAME }}" \
+ --resource-group "${{ env.RG_NAME }}" \
+ --query "publicNetworkAccess" \
+ -o tsv)
+ echo "ORIGINAL_SQL_PUBLIC_ACCESS=$ORIGINAL_SQL_PUBLIC_ACCESS" >> $GITHUB_ENV
+
+ if [ "$ORIGINAL_SQL_PUBLIC_ACCESS" != "Enabled" ]; then
+ az sql server update \
+ --name "${{ env.SQL_SERVER_NAME }}" \
+ --resource-group "${{ env.RG_NAME }}" \
+ --enable-public-network true \
+ --output none
+ echo "✓ SQL Server public access enabled"
+ fi
+
+ # Add temporary firewall rule allowing all IPs
+ EXISTING_ALLOW_ALL_RULE=$(az sql server firewall-rule list \
+ --server "${{ env.SQL_SERVER_NAME }}" \
+ --resource-group "${{ env.RG_NAME }}" \
+ --query "[?name=='temp-allow-all-ip'] | [0].name" \
+ -o tsv 2>/dev/null || echo "")
+
+ if [ -z "$EXISTING_ALLOW_ALL_RULE" ]; then
+ az sql server firewall-rule create \
+ --resource-group "${{ env.RG_NAME }}" \
+ --server "${{ env.SQL_SERVER_NAME }}" \
+ --name "temp-allow-all-ip" \
+ --start-ip-address 0.0.0.0 \
+ --end-ip-address 255.255.255.255 \
+ --output none || echo "⚠ Warning: Failed to create allow-all firewall rule"
+ echo "CREATED_SQL_FIREWALL_RULE=true" >> $GITHUB_ENV
+ echo "✓ Temporary allow-all firewall rule created"
+ else
+ echo "CREATED_SQL_FIREWALL_RULE=false" >> $GITHUB_ENV
+ fi
+
+ echo "Waiting for network access changes to propagate..."
+ sleep 30
+ echo "=== Public network access enabled successfully ==="
+
+ - name: Import Sample Data (Post-Deployment Scripts)
run: |
set -e
az account set --subscription "${{ secrets.AZURE_SUBSCRIPTION_ID }}"
@@ -227,55 +448,87 @@ jobs:
"${{ env.SQL_DATABASE }}" \
"$user_roles_json" \
"${{ secrets.AZURE_CLIENT_ID }}"
+
echo "=== Post-Deployment Script Completed Successfully ==="
- - name: Get AI Services name and store in variable
- if: always() && steps.check_create_rg.outcome == 'success'
- id: get_ai_services_name
+ - name: Restore Network Access (WAF Only)
+ if: always() && env.DEPLOYMENT_TYPE == 'WAF'
run: |
set -e
- echo "Getting AI Services name..."
- # Get the AI Services name
- ai_services_name=$(az cognitiveservices account list -g ${{ env.RESOURCE_GROUP_NAME }} --query "[0].name" -o tsv)
- if [ -z "$ai_services_name" ]; then
- echo "No AI Services resource found in the resource group."
- echo "AI_SERVICES_NAME=" >> $GITHUB_OUTPUT
- else
- echo "AI_SERVICES_NAME=${ai_services_name}" >> $GITHUB_OUTPUT
- echo "Found AI Services resource: $ai_services_name"
+ echo "=== Restoring original network access settings ==="
+
+ # Restore Storage Account access
+ if [ -n "${{ env.ORIGINAL_STORAGE_PUBLIC_ACCESS }}" ] && [ "${{ env.ORIGINAL_STORAGE_PUBLIC_ACCESS }}" != "Enabled" ]; then
+ echo "Restoring Storage Account public access to: ${{ env.ORIGINAL_STORAGE_PUBLIC_ACCESS }}"
+ RESTORE_VALUE="${{ env.ORIGINAL_STORAGE_PUBLIC_ACCESS }}"
+ az storage account update \
+ --name "${{ env.STORAGE_ACCOUNT }}" \
+ --resource-group "${{ env.RG_NAME }}" \
+ --public-network-access "$RESTORE_VALUE" \
+ --output none || echo "✗ Failed to restore Storage Account access"
+ echo "✓ Storage Account access restored"
+ fi
+
+ # Restore Storage Account network default action
+ if [ -n "${{ env.ORIGINAL_STORAGE_DEFAULT_ACTION }}" ] && [ "${{ env.ORIGINAL_STORAGE_DEFAULT_ACTION }}" != "Allow" ]; then
+ echo "Restoring Storage Account network default action to: ${{ env.ORIGINAL_STORAGE_DEFAULT_ACTION }}"
+ az storage account update \
+ --name "${{ env.STORAGE_ACCOUNT }}" \
+ --resource-group "${{ env.RG_NAME }}" \
+ --default-action "${{ env.ORIGINAL_STORAGE_DEFAULT_ACTION }}" \
+ --output none || echo "✗ Failed to restore Storage Account network default action"
+ echo "✓ Storage Account network default action restored"
+ fi
+
+ # Restore AI Foundry access
+ if [ -n "${{ env.ORIGINAL_FOUNDRY_PUBLIC_ACCESS }}" ] && [ "${{ env.ORIGINAL_FOUNDRY_PUBLIC_ACCESS }}" != "Enabled" ]; then
+ echo "Restoring AI Foundry public access to: ${{ env.ORIGINAL_FOUNDRY_PUBLIC_ACCESS }}"
+ az resource update \
+ --ids "${{ env.AIF_ACCOUNT_RESOURCE_ID }}" \
+ --api-version 2024-10-01 \
+ --set properties.publicNetworkAccess="${{ env.ORIGINAL_FOUNDRY_PUBLIC_ACCESS }}" \
+ --set properties.apiProperties.qnaAzureSearchEndpointKey="" \
+ --set properties.networkAcls.bypass="AzureServices" \
+ --output none 2>/dev/null || echo "⚠ Warning: Failed to restore AI Foundry access automatically"
+ echo "✓ AI Foundry access restored"
+ fi
+
+ # Restore Key Vault access
+ if [ -n "${{ env.ORIGINAL_KEYVAULT_PUBLIC_ACCESS }}" ] && [ "${{ env.ORIGINAL_KEYVAULT_PUBLIC_ACCESS }}" != "Enabled" ]; then
+ echo "Restoring Key Vault public access to: ${{ env.ORIGINAL_KEYVAULT_PUBLIC_ACCESS }}"
+ RESTORE_VALUE="${{ env.ORIGINAL_KEYVAULT_PUBLIC_ACCESS }}"
+ az keyvault update \
+ --name "${{ env.KEYVAULT_NAME }}" \
+ --resource-group "${{ env.RG_NAME }}" \
+ --public-network-access "$RESTORE_VALUE" \
+ --output none || echo "✗ Failed to restore Key Vault access"
+ echo "✓ Key Vault access restored"
fi
- - name: List KeyVaults and Store in Array
- if: always() && steps.check_create_rg.outcome == 'success'
- id: list_keyvaults
- run: |
- set -e
- echo "Listing all KeyVaults in the resource group ${{ env.RESOURCE_GROUP_NAME }}..."
- # Get the list of KeyVaults in the specified resource group
- keyvaults=$(az resource list --resource-group ${{ env.RESOURCE_GROUP_NAME }} --query "[?type=='Microsoft.KeyVault/vaults'].name" -o tsv)
+ # Restore SQL Server public access
+ if [ -n "${{ env.ORIGINAL_SQL_PUBLIC_ACCESS }}" ] && [ "${{ env.ORIGINAL_SQL_PUBLIC_ACCESS }}" != "Enabled" ]; then
+ echo "Restoring SQL Server public access to: ${{ env.ORIGINAL_SQL_PUBLIC_ACCESS }}"
+ az sql server update \
+ --name "${{ env.SQL_SERVER_NAME }}" \
+ --resource-group "${{ env.RG_NAME }}" \
+ --enable-public-network false \
+ --output none || echo "✗ Failed to restore SQL Server access"
+ echo "✓ SQL Server access restored"
+ fi
- if [ -z "$keyvaults" ]; then
- echo "No KeyVaults found in resource group ${{ env.RESOURCE_GROUP_NAME }}."
- echo "KEYVAULTS=[]" >> $GITHUB_OUTPUT # If no KeyVaults found, set an empty array
- else
- echo "KeyVaults found: $keyvaults"
-
- # Format the list into an array with proper formatting (no trailing comma)
- keyvault_array="["
- first=true
- for kv in $keyvaults; do
- if [ "$first" = true ]; then
- keyvault_array="$keyvault_array\"$kv\""
- first=false
- else
- keyvault_array="$keyvault_array,\"$kv\""
- fi
- done
- keyvault_array="$keyvault_array]"
-
- # Output the formatted array and save it to the job output
- echo "KEYVAULTS=$keyvault_array" >> $GITHUB_OUTPUT
+ # Remove temporary firewall rule if we created it
+ if [ "${{ env.CREATED_SQL_FIREWALL_RULE }}" == "true" ]; then
+ echo "Removing temporary allow-all firewall rule..."
+ az sql server firewall-rule delete \
+ --resource-group "${{ env.RG_NAME }}" \
+ --server "${{ env.SQL_SERVER_NAME }}" \
+ --name "temp-allow-all-ip" \
+ --output none || echo "⚠ Warning: Failed to remove temporary firewall rule"
+ echo "✓ Temporary firewall rule removed"
fi
+
+ echo "=== Network access restoration completed ==="
+
- name: Set Deployment Status
id: deployment_status
if: always()
@@ -288,7 +541,9 @@ jobs:
- name: Logout
if: always()
- run: az logout
+ run: |
+ az logout
+ azd auth logout
e2e-test:
needs: deploy
@@ -299,189 +554,59 @@ jobs:
secrets: inherit
cleanup:
- if: always()
+ if: always() && needs.deploy.outputs.RESOURCE_GROUP_NAME != ''
needs: [deploy, e2e-test]
runs-on: ubuntu-latest
env:
RESOURCE_GROUP_NAME: ${{ needs.deploy.outputs.RESOURCE_GROUP_NAME }}
- AI_SERVICES_NAME: ${{ needs.deploy.outputs.AI_SERVICES_NAME }}
- KEYVAULTS: ${{ needs.deploy.outputs.KEYVAULTS }}
AZURE_LOCATION: ${{ needs.deploy.outputs.AZURE_LOCATION }}
- SOLUTION_PREFIX: ${{ needs.deploy.outputs.SOLUTION_PREFIX }}
+ ENV_NAME: ${{ needs.deploy.outputs.ENV_NAME }}
+ DEPLOYMENT_TYPE: ${{ needs.deploy.outputs.DEPLOYMENT_TYPE }}
steps:
+ - name: Checkout Code
+ uses: actions/checkout@v4
+
- name: Setup Azure CLI
- run: curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash
+ run: |
+ curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash
+ az --version # Verify installation
+
+ - name: Setup Azure Developer CLI
+ run: |
+ curl -fsSL https://aka.ms/install-azd.sh | sudo bash
+ azd version
- name: Login to Azure
run: |
+ echo "Logging into Azure..."
az login --service-principal -u ${{ secrets.AZURE_CLIENT_ID }} -p ${{ secrets.AZURE_CLIENT_SECRET }} --tenant ${{ secrets.AZURE_TENANT_ID }}
+ azd auth login --client-id ${{ secrets.AZURE_CLIENT_ID }} --client-secret ${{ secrets.AZURE_CLIENT_SECRET }} --tenant-id ${{ secrets.AZURE_TENANT_ID }}
+ echo "Setting default subscription..."
az account set --subscription ${{ secrets.AZURE_SUBSCRIPTION_ID }}
+ azd config set defaults.subscription ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- - name: Delete Bicep Deployment
- if: always()
+ - name: Select Environment
run: |
- set -e
- echo "Checking if resource group exists..."
- echo "Resource group name: ${{ env.RESOURCE_GROUP_NAME }}"
- if [ -z "${{ env.RESOURCE_GROUP_NAME }}" ]; then
- echo "Resource group name is empty. Skipping deletion."
- exit 0
- fi
- rg_exists=$(az group exists --name "${{ env.RESOURCE_GROUP_NAME }}")
- if [ "$rg_exists" = "true" ]; then
- echo "Resource group exists. Cleaning..."
- az group delete \
- --name "${{ env.RESOURCE_GROUP_NAME }}" \
- --yes \
- --no-wait
- echo "Resource group deletion initiated: ${{ env.RESOURCE_GROUP_NAME }}"
+ set -e
+ # Try to select the environment if it exists, otherwise create a minimal environment for cleanup
+ azd env list
+ if azd env list | grep -q "${{ env.ENV_NAME }}"; then
+ echo "Environment ${{ env.ENV_NAME }} found, selecting it..."
+ azd env select ${{ env.ENV_NAME }}
else
- echo "Resource group does not exist."
- fi
-
- - name: Wait for resource deletion to complete
- if: always()
+ echo "Environment ${{ env.ENV_NAME }} not found, creating minimal environment for cleanup..."
+ azd env new ${{ env.ENV_NAME }} --no-prompt
+ azd env set AZURE_RESOURCE_GROUP "${{ env.RESOURCE_GROUP_NAME }}"
+ azd env set AZURE_SUBSCRIPTION_ID "${{ secrets.AZURE_SUBSCRIPTION_ID }}"
+ azd env set AZURE_ENV_AI_SERVICE_LOCATION="${{ env.AZURE_LOCATION }}"
+ fi
+
+ - name: Delete deployment using azd
run: |
- # Check if resource group name is available
- if [ -z "${{ env.RESOURCE_GROUP_NAME }}" ]; then
- echo "Resource group name is empty. Skipping resource check."
- exit 0
- fi
-
- # List of keyvaults
- KEYVAULTS="${{ env.KEYVAULTS }}"
-
- # Remove the surrounding square brackets and quotes, if they exist
- stripped_keyvaults=$(echo "$KEYVAULTS" | sed 's/\[\|\]//g' | sed 's/"//g')
- # Convert the comma-separated string into an array
- IFS=',' read -r -a resources_to_check <<< "$stripped_keyvaults"
- echo "List of resources to check: ${resources_to_check[@]}"
- # Check if resource group still exists before listing resources
- rg_exists=$(az group exists --name "${{ env.RESOURCE_GROUP_NAME }}")
- if [ "$rg_exists" = "false" ]; then
- echo "Resource group no longer exists. Skipping resource check."
- exit 0
- fi
-
- # Get the list of resources in YAML format
- resource_list=$(az resource list --resource-group "${{ env.RESOURCE_GROUP_NAME }}" --output yaml || echo "")
-
- # Maximum number of retries
- max_retries=3
-
- # Retry intervals in seconds (30, 60, 120)
- retry_intervals=(30 60 120)
-
- # Retry mechanism to check resources
- retries=0
- while true; do
- resource_found=false
-
- # Check if resource group still exists
- rg_exists=$(az group exists --name "${{ env.RESOURCE_GROUP_NAME }}")
- if [ "$rg_exists" = "false" ]; then
- echo "Resource group no longer exists. Exiting resource check."
- break
- fi
-
- # Iterate through the resources to check
- for resource in "${resources_to_check[@]}"; do
- # Skip empty resource names
- if [ -z "$resource" ]; then
- continue
- fi
-
- echo "Checking resource: $resource"
- if echo "$resource_list" | grep -q "name: $resource"; then
- echo "Resource '$resource' exists in the resource group."
- resource_found=true
- else
- echo "Resource '$resource' does not exist in the resource group."
- fi
- done
-
- # If any resource exists, retry
- if [ "$resource_found" = true ]; then
- retries=$((retries + 1))
- if [ "$retries" -ge "$max_retries" ]; then
- echo "Maximum retry attempts reached. Exiting."
- break
- else
- # Wait for the appropriate interval for the current retry
- echo "Waiting for ${retry_intervals[$retries-1]} seconds before retrying..."
- sleep ${retry_intervals[$retries-1]}
- # Refresh resource list
- resource_list=$(az resource list --resource-group "${{ env.RESOURCE_GROUP_NAME }}" --output yaml || echo "")
- fi
- else
- echo "No resources found. Exiting."
- break
- fi
- done
-
- - name: Purging the Resources
- if: always()
- run: |
- set -e
-
- # Check if resource group name is available
- if [ -z "${{ env.RESOURCE_GROUP_NAME }}" ]; then
- echo "Resource group name is empty. Skipping resource purging."
- exit 0
- fi
-
- # Purge AI Services
- if [ -z "${{ env.AI_SERVICES_NAME }}" ]; then
- echo "AI_SERVICES_NAME is not set. Skipping AI Services purge."
- else
- echo "Purging AI Services..."
- if [ -n "$(az cognitiveservices account list-deleted --query "[?name=='${{ env.AI_SERVICES_NAME }}']" -o tsv)" ]; then
- echo "AI Services '${{ env.AI_SERVICES_NAME }}' is soft-deleted. Proceeding to purge..."
- az cognitiveservices account purge --location "${{ env.AZURE_LOCATION }}" --resource-group "${{ env.RESOURCE_GROUP_NAME }}" --name "${{ env.AI_SERVICES_NAME }}"
- else
- echo "AI Services '${{ env.AI_SERVICES_NAME }}' is not soft-deleted. No action taken."
- fi
- fi
-
- # Ensure KEYVAULTS is properly formatted as a comma-separated string
- KEYVAULTS="${{ env.KEYVAULTS }}"
-
- # Check if KEYVAULTS is empty or null
- if [ -z "$KEYVAULTS" ] || [ "$KEYVAULTS" = "[]" ]; then
- echo "No KeyVaults to purge."
- exit 0
- fi
-
- # Remove the surrounding square brackets and quotes, if they exist
- stripped_keyvaults=$(echo "$KEYVAULTS" | sed 's/\[\|\]//g' | sed 's/"//g')
- # Convert the comma-separated string into an array
- IFS=',' read -r -a keyvault_array <<< "$stripped_keyvaults"
-
- echo "Using KeyVaults Array..."
- for keyvault_name in "${keyvault_array[@]}"; do
- # Skip empty keyvault names
- if [ -z "$keyvault_name" ]; then
- continue
- fi
-
- echo "Processing KeyVault: $keyvault_name"
- # Check if the KeyVault is soft-deleted
- deleted_vaults=$(az keyvault list-deleted --query "[?name=='$keyvault_name']" -o json --subscription ${{ secrets.AZURE_SUBSCRIPTION_ID }})
-
- # If the KeyVault is found in the soft-deleted state, purge it
- if [ "$(echo "$deleted_vaults" | jq length)" -gt 0 ]; then
- echo "KeyVault '$keyvault_name' is soft-deleted. Proceeding to purge..."
- az keyvault purge --name "$keyvault_name" --no-wait
- else
- echo "KeyVault '$keyvault_name' is not soft-deleted. No action taken."
- fi
- done
-
- echo "Resource purging completed successfully"
-
- - name: Logout
- if: always()
- run: az logout
+ set -e
+ echo "Deleting deployment..."
+ azd down --purge --force --no-prompt
+ echo "Deployment deleted successfully."
- name: Notify on Failure
if: failure() || needs.deploy.result == 'failure' || needs.e2e-test.result == 'failure'
@@ -490,7 +615,7 @@ jobs:
# Construct the email body
EMAIL_BODY=$(cat <Dear Team,
We would like to inform you that the Build-your-own-copilot-Solution-Accelerator(Client Advisior) Automation process has encountered an issue and has failed to complete successfully.
Build URL: ${RUN_URL}
${OUTPUT}
Please investigate the matter at your earliest convenience.
Best regards,
Your Automation Team
"
+ "body": "Dear Team,
We would like to inform you that the Build-your-own-copilot-Solution-Accelerator(Client Advisior) - ${{ env.DEPLOYMENT_TYPE }} Automation process has encountered an issue and has failed to complete successfully.
Build URL: ${RUN_URL}
${OUTPUT}
Please investigate the matter at your earliest convenience.
Best regards,
Your Automation Team
"
}
EOF
)
@@ -498,3 +623,10 @@ jobs:
curl -X POST "${{ secrets.LOGIC_APP_URL }}" \
-H "Content-Type: application/json" \
-d "$EMAIL_BODY" || echo "Failed to send notification"
+
+ - name: Logout
+ if: always()
+ run: |
+ echo "Logging out of Azure..."
+ az logout
+ azd auth logout
diff --git a/.github/workflows/Create-Release.yml b/.github/workflows/Create-Release.yml
deleted file mode 100644
index 8ddc259ac..000000000
--- a/.github/workflows/Create-Release.yml
+++ /dev/null
@@ -1,65 +0,0 @@
-on:
- push:
- branches:
- - main
-
-permissions:
- contents: write
- pull-requests: write
-
-name: Create-Release
-
-jobs:
- create-release:
- runs-on: ubuntu-latest
- steps:
- - name: Checkout
- uses: actions/checkout@v4
- with:
- ref: ${{ github.event.workflow_run.head_sha }}
-
- - uses: codfish/semantic-release-action@v3
- id: semantic
- with:
- tag-format: 'v${version}'
- additional-packages: |
- ['conventional-changelog-conventionalcommits@7']
- plugins: |
- [
- [
- "@semantic-release/commit-analyzer",
- {
- "preset": "conventionalcommits"
- }
- ],
- [
- "@semantic-release/release-notes-generator",
- {
- "preset": "conventionalcommits",
- "presetConfig": {
- "types": [
- { type: 'feat', section: 'Features', hidden: false },
- { type: 'fix', section: 'Bug Fixes', hidden: false },
- { type: 'perf', section: 'Performance Improvements', hidden: false },
- { type: 'revert', section: 'Reverts', hidden: false },
- { type: 'docs', section: 'Other Updates', hidden: false },
- { type: 'style', section: 'Other Updates', hidden: false },
- { type: 'chore', section: 'Other Updates', hidden: false },
- { type: 'refactor', section: 'Other Updates', hidden: false },
- { type: 'test', section: 'Other Updates', hidden: false },
- { type: 'build', section: 'Other Updates', hidden: false },
- { type: 'ci', section: 'Other Updates', hidden: false }
- ]
- }
- }
- ],
- '@semantic-release/github'
- ]
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- - run: echo ${{ steps.semantic.outputs.release-version }}
-
- - run: echo "$OUTPUTS"
- env:
- OUTPUTS: ${{ toJson(steps.semantic.outputs) }}
-
diff --git a/README.md b/README.md
index c7762f03a..3707457ef 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,9 @@
+
+
+ ⚠️ This repository is no longer maintained ⚠️
+
+
+
# Build your own copilot solution accelerator
This solution accelerator is a powerful tool that helps you create your own copilots. The accelerator can be used by any customer looking for reusable architecture and code snippets to build custom copilots with their own enterprise data.
@@ -192,9 +198,17 @@ Check out similar solution accelerators
| [Content processing](https://github.com/microsoft/content-processing-solution-accelerator) | Programmatically extract data and apply schemas to unstructured documents across text-based and multi-modal content using Azure AI Foundry, Azure OpenAI, Azure AI Content Understanding, and Azure Cosmos DB. |
-
+💡 Want to get familiar with Microsoft's AI and Data Engineering best practices? Check out our playbooks to learn more
+
+| Playbook | Description |
+|:---|:---|
+| [AI playbook](https://learn.microsoft.com/en-us/ai/playbook/) | The Artificial Intelligence (AI) Playbook provides enterprise software engineers with solutions, capabilities, and code developed to solve real-world AI problems. |
+| [Data playbook](https://learn.microsoft.com/en-us/data-engineering/playbook/understanding-data-playbook) | The data playbook provides enterprise software engineers with solutions which contain code developed to solve real-world problems. Everything in the playbook is developed with, and validated by, some of Microsoft's largest and most influential customers and partners. |
+
+
+
### Version history
An updated version of the **Build Your Own Copilot** solution accelerator was published on `04/24/2025`. If you deployed the accelerator prior to that date, please note the following changes:
diff --git a/docs/CustomizingAzdParameters.md b/docs/CustomizingAzdParameters.md
index b74a1b48a..6abe3728b 100644
--- a/docs/CustomizingAzdParameters.md
+++ b/docs/CustomizingAzdParameters.md
@@ -10,7 +10,8 @@ By default this template will use the environment name as the prefix to prevent
| Name | Type | Default Value | Purpose |
| -----------------------------| ------- | ------------------- | ---------------------------------------------------------------------------------------------------- |
| `AZURE_ENV_NAME` | string | `azdtemp` | Used as a prefix for all resource names to ensure uniqueness across environments. |
-| `AZURE_ENV_COSMOS_LOCATION` | string | `eastus2` | Location of the Cosmos DB instance. Choose from (allowed values: `swedencentral`, `australiaeast`). |
+| `AZURE_ENV_COSMOS_LOCATION` | string | `Same as resource group location` | Primary location for the Cosmos DB instance. When enabling redundancy, verify the region supports zone redundancy. [Check supported regions](https://learn.microsoft.com/en-us/azure/reliability/regions-list). |
+| `AZURE_ENV_COSMOS_SECONDARY_LOCATION` | string | `canadacentral` | Secondary failover location for Cosmos DB when enableRedundancy is true. [Check supported regions](https://learn.microsoft.com/en-us/azure/reliability/regions-list). |
| `AZURE_ENV_MODEL_DEPLOYMENT_TYPE` | string | `GlobalStandard` | Change the Model Deployment Type (allowed values: Standard, GlobalStandard). |
| `AZURE_ENV_MODEL_NAME` | string | `gpt-4o-mini` | Set the GPT model name (allowed values: `gpt-4o`). |
| `AZURE_ENV_MODEL_VERSION` | string | `2025-01-01-preview` | Set the Azure OpenAI API version (allowed values: 2024-08-06). |
diff --git a/docs/DeploymentGuide.md b/docs/DeploymentGuide.md
index c37013306..caf3d8e67 100644
--- a/docs/DeploymentGuide.md
+++ b/docs/DeploymentGuide.md
@@ -236,7 +236,8 @@ Once you've opened the project in [Codespaces](#github-codespaces), [Dev Contain
5. Once the deployment is complete, please follow the [Import Sample Data](#post-deployment-steps) instructions under **Post Deployment Steps** to load the sample data correctly.
6. Open the [Azure Portal](https://portal.azure.com/), go to the deployed resource group, find the App Service and get the app URL from `Default domain`.
7. Test the app locally with the sample question with any selected client: _Show latest asset value by asset type?_. For more sample questions you can test in the application, see [Sample Questions](SampleQuestions.md).
-8. You can now delete the resources by running `azd down`, if you are done trying out the application.
+8. You can now delete the resources by running `azd down`, if you are done trying out the application.
+ > **Note:** If you deployed with `enableRedundancy=true` and Log Analytics workspace replication is enabled, you must first disable replication before running `azd down`, else resource group delete will fail. Follow the steps in [Handling Log Analytics Workspace Deletion with Replication Enabled](./LogAnalyticsReplicationDisable.md), wait until replication returns `false`, then run `azd down`.
### Publishing Local Build Container to Azure Container Registry
diff --git a/docs/LogAnalyticsReplicationDisable.md b/docs/LogAnalyticsReplicationDisable.md
new file mode 100644
index 000000000..0772b97b3
--- /dev/null
+++ b/docs/LogAnalyticsReplicationDisable.md
@@ -0,0 +1,28 @@
+# 🛠 Handling Log Analytics Workspace Deletion with Replication Enabled
+
+If redundancy (replication) is enabled for your Log Analytics workspace, you must disable it before deleting the workspace or resource group. Otherwise, deletion will fail.
+
+## ✅ Steps to Disable Replication Before Deletion
+Run the following Azure CLI command. Note: This operation may take about 5 minutes to complete.
+
+```bash
+az resource update --ids "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.OperationalInsights/workspaces/{logAnalyticsName}" --set properties.replication.enabled=false
+```
+
+Replace:
+- `{subscriptionId}` → Your Azure subscription ID
+- `{resourceGroupName}` → The name of your resource group
+- `{logAnalyticsName}` → The name of your Log Analytics workspace
+
+Optional: Verify replication is disabled (should output `false`):
+```bash
+az resource show --ids "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.OperationalInsights/workspaces/{logAnalyticsName}" --query properties.replication.enabled -o tsv
+```
+
+## ✅ After Disabling Replication
+You can safely delete:
+- The Log Analytics workspace (manual)
+- The resource group (manual), or
+- All provisioned resources via `azd down`
+
+Return to: [Deployment Guide](./DeploymentGuide.md)
diff --git a/infra/main.bicep b/infra/main.bicep
index 1acdaec7f..be6629585 100644
--- a/infra/main.bicep
+++ b/infra/main.bicep
@@ -10,7 +10,10 @@ param solutionName string = 'clientadvisor'
param existingLogAnalyticsWorkspaceId string = ''
@description('Optional. CosmosDB Location')
-param cosmosLocation string = 'eastus2'
+param cosmosLocation string = resourceGroup().location
+
+@description('Optional. Secondary CosmosDB Location for high availability and failover scenarios. Not all Azure regions support zone redundancy for Cosmos DB. See https://learn.microsoft.com/azure/cosmos-db/high-availability#azure-regions-and-zone-redundancy for supported regions.')
+param secondaryCosmosLocation string = 'canadacentral'
@minLength(1)
@description('Optional. GPT model deployment type:')
@@ -231,20 +234,6 @@ var replicaLocation = replicaRegionPairs[resourceGroup().location]
@description('Optional. The tags to apply to all deployed Azure resources.')
param tags resourceInput<'Microsoft.Resources/resourceGroups@2025-04-01'>.tags = {}
-// Region pairs list based on article in [Azure Database for MySQL Flexible Server - Azure Regions](https://learn.microsoft.com/azure/mysql/flexible-server/overview#azure-regions) for supported high availability regions for CosmosDB.
-var cosmosDbZoneRedundantHaRegionPairs = {
- australiaeast: 'uksouth' //'southeastasia'
- centralus: 'eastus2'
- eastasia: 'southeastasia'
- eastus: 'centralus'
- eastus2: 'centralus'
- japaneast: 'australiaeast'
- northeurope: 'westeurope'
- southeastasia: 'eastasia'
- uksouth: 'westeurope'
- westeurope: 'northeurope'
-}
-
var allTags = union(
{
'azd-env-name': solutionName
@@ -252,9 +241,6 @@ var allTags = union(
tags
)
-// Paired location calculated based on 'location' parameter. This location will be used by applicable resources if `enableScalability` is set to `true`
-var cosmosDbHaLocation = cosmosDbZoneRedundantHaRegionPairs[resourceGroup().location]
-
// Extracts subscription, resource group, and workspace name from the resource ID when using an existing Log Analytics workspace
var useExistingLogAnalytics = !empty(existingLogAnalyticsWorkspaceId)
@@ -557,7 +543,7 @@ module keyvault 'br/public:avm/res/key-vault/vault:0.12.1' = {
name: keyVaultName
location: solutionLocation
tags: tags
- sku: 'standard'
+ sku: enableScalability ? 'premium' : 'standard'
publicNetworkAccess: enablePrivateNetworking ? 'Disabled' : 'Enabled'
networkAcls: {
defaultAction: 'Allow'
@@ -843,12 +829,12 @@ module cosmosDb 'br/public:avm/res/document-db/database-account:0.15.0' = {
{
failoverPriority: 0
isZoneRedundant: true
- locationName: solutionLocation
+ locationName: cosmosLocation
}
{
failoverPriority: 1
isZoneRedundant: true
- locationName: cosmosDbHaLocation
+ locationName: secondaryCosmosLocation
}
]
: [
@@ -1014,22 +1000,6 @@ module sqlDBModule 'br/public:avm/res/sql/server:0.20.1' = {
]
}
primaryUserAssignedIdentityResourceId: userAssignedIdentity.outputs.resourceId
- privateEndpoints: enablePrivateNetworking
- ? [
- {
- privateDnsZoneGroup: {
- privateDnsZoneGroupConfigs: [
- {
- privateDnsZoneResourceId: avmPrivateDnsZones[dnsZoneIndex.sqlServer]!.outputs.resourceId
- }
- ]
- }
- service: 'sqlServer'
- subnetResourceId: virtualNetwork!.outputs.pepsSubnetResourceId
- tags: tags
- }
- ]
- : []
firewallRules: (!enablePrivateNetworking) ? [
{
endIpAddress: '255.255.255.255'
@@ -1045,6 +1015,34 @@ module sqlDBModule 'br/public:avm/res/sql/server:0.20.1' = {
tags: tags
}
}
+// ========== SQL Server Private Endpoint (separated) ========== //
+module sqlDbPrivateEndpoint 'br/public:avm/res/network/private-endpoint:0.11.1' = if (enablePrivateNetworking) {
+ name: take('avm.res.network.private-endpoint.sql-${solutionSuffix}', 64)
+ params: {
+ name: 'pep-sql-${solutionSuffix}'
+ location: solutionLocation
+ tags: tags
+ enableTelemetry: enableTelemetry
+ subnetResourceId: virtualNetwork!.outputs.pepsSubnetResourceId
+ customNetworkInterfaceName: 'nic-sql-${solutionSuffix}'
+ privateLinkServiceConnections: [
+ {
+ name: 'pl-sqlserver-${solutionSuffix}'
+ properties: {
+ privateLinkServiceId: sqlDBModule.outputs.resourceId
+ groupIds: ['sqlServer']
+ }
+ }
+ ]
+ privateDnsZoneGroup: {
+ privateDnsZoneGroupConfigs: [
+ {
+ privateDnsZoneResourceId: avmPrivateDnsZones[dnsZoneIndex.sqlServer]!.outputs.resourceId
+ }
+ ]
+ }
+ }
+}
// ========== Frontend server farm ========== //
// WAF best practices for Web Application Services: https://learn.microsoft.com/en-us/azure/well-architected/service-guides/app-service-web-apps
@@ -1062,11 +1060,11 @@ module webServerFarm 'br/public:avm/res/web/serverfarm:0.5.0' = {
// WAF aligned configuration for Monitoring
diagnosticSettings: enableMonitoring ? [{ workspaceResourceId: logAnalyticsWorkspaceResourceId }] : null
// WAF aligned configuration for Scalability
- skuName: enableScalability || enableRedundancy ? 'P1v3' : 'B3'
+ skuName: 'B3'
// skuCapacity: enableScalability ? 3 : 1
skuCapacity: 1 // skuCapacity set to 1 (not 3) due to multiple agents created per type during WAF deployment
// WAF aligned configuration for Redundancy
- zoneRedundant: enableRedundancy ? true : false
+ zoneRedundant: false // zone redundancy requires a minimum of 2 instances; as we are keeping skuCapacity to 1, setting zoneRedundant to false
}
}
@@ -1218,7 +1216,7 @@ module searchService 'br/public:avm/res/search/search-service:0.11.1' = {
]
partitionCount: 1
replicaCount: 1
- sku: 'standard'
+ sku: enableScalability ? 'standard' : 'basic'
semanticSearch: 'free'
// Use the deployment tags provided to the template
tags: tags
diff --git a/infra/main.json b/infra/main.json
index 515dc388d..d3ad65498 100644
--- a/infra/main.json
+++ b/infra/main.json
@@ -5,8 +5,8 @@
"metadata": {
"_generator": {
"name": "bicep",
- "version": "0.37.4.10188",
- "templateHash": "10200691163213391924"
+ "version": "0.39.26.7824",
+ "templateHash": "4551936874633515152"
}
},
"parameters": {
@@ -28,11 +28,18 @@
},
"cosmosLocation": {
"type": "string",
- "defaultValue": "eastus2",
+ "defaultValue": "[resourceGroup().location]",
"metadata": {
"description": "Optional. CosmosDB Location"
}
},
+ "secondaryCosmosLocation": {
+ "type": "string",
+ "defaultValue": "canadacentral",
+ "metadata": {
+ "description": "Optional. Secondary CosmosDB Location for high availability and failover scenarios. Not all Azure regions support zone redundancy for Cosmos DB. See https://learn.microsoft.com/azure/cosmos-db/high-availability#azure-regions-and-zone-redundancy for supported regions."
+ }
+ },
"gptModelDeploymentType": {
"type": "string",
"defaultValue": "GlobalStandard",
@@ -307,20 +314,7 @@
"westeurope": "northeurope"
},
"replicaLocation": "[variables('replicaRegionPairs')[resourceGroup().location]]",
- "cosmosDbZoneRedundantHaRegionPairs": {
- "australiaeast": "uksouth",
- "centralus": "eastus2",
- "eastasia": "southeastasia",
- "eastus": "centralus",
- "eastus2": "centralus",
- "japaneast": "australiaeast",
- "northeurope": "westeurope",
- "southeastasia": "eastasia",
- "uksouth": "westeurope",
- "westeurope": "northeurope"
- },
"allTags": "[union(createObject('azd-env-name', parameters('solutionName')), parameters('tags'))]",
- "cosmosDbHaLocation": "[variables('cosmosDbZoneRedundantHaRegionPairs')[resourceGroup().location]]",
"useExistingLogAnalytics": "[not(empty(parameters('existingLogAnalyticsWorkspaceId')))]",
"logAnalyticsWorkspaceResourceName": "[format('log-{0}', variables('solutionSuffix'))]",
"applicationInsightsResourceName": "[format('appi-{0}', variables('solutionSuffix'))]",
@@ -456,7 +450,7 @@
"logAnalyticsWorkspace": {
"condition": "[and(parameters('enableMonitoring'), not(variables('useExistingLogAnalytics')))]",
"type": "Microsoft.Resources/deployments",
- "apiVersion": "2022-09-01",
+ "apiVersion": "2025-04-01",
"name": "[take(format('avm.res.operational-insights.workspace.{0}', variables('logAnalyticsWorkspaceResourceName')), 64)]",
"properties": {
"expressionEvaluationOptions": {
@@ -3562,7 +3556,7 @@
"applicationInsights": {
"condition": "[parameters('enableMonitoring')]",
"type": "Microsoft.Resources/deployments",
- "apiVersion": "2022-09-01",
+ "apiVersion": "2025-04-01",
"name": "[take(format('avm.res.insights.component.{0}', variables('applicationInsightsResourceName')), 64)]",
"properties": {
"expressionEvaluationOptions": {
@@ -4292,7 +4286,7 @@
},
"userAssignedIdentity": {
"type": "Microsoft.Resources/deployments",
- "apiVersion": "2022-09-01",
+ "apiVersion": "2025-04-01",
"name": "[take(format('avm.res.managed-identity.user-assigned-identity.{0}', variables('userAssignedIdentityResourceName')), 64)]",
"properties": {
"expressionEvaluationOptions": {
@@ -4774,7 +4768,7 @@
},
"sqlUserAssignedIdentity": {
"type": "Microsoft.Resources/deployments",
- "apiVersion": "2022-09-01",
+ "apiVersion": "2025-04-01",
"name": "[take(format('avm.res.managed-identity.user-assigned-identity.{0}', variables('sqlUserAssignedIdentityResourceName')), 64)]",
"properties": {
"expressionEvaluationOptions": {
@@ -5257,7 +5251,7 @@
"virtualNetwork": {
"condition": "[parameters('enablePrivateNetworking')]",
"type": "Microsoft.Resources/deployments",
- "apiVersion": "2022-09-01",
+ "apiVersion": "2025-04-01",
"name": "[take(format('module.virtualNetwork.{0}', variables('solutionSuffix')), 64)]",
"properties": {
"expressionEvaluationOptions": {
@@ -5294,8 +5288,8 @@
"metadata": {
"_generator": {
"name": "bicep",
- "version": "0.37.4.10188",
- "templateHash": "15908341678380884075"
+ "version": "0.39.26.7824",
+ "templateHash": "14641679443140532549"
}
},
"definitions": {
@@ -5688,7 +5682,7 @@
},
"condition": "[not(empty(tryGet(parameters('subnets')[copyIndex()], 'networkSecurityGroup')))]",
"type": "Microsoft.Resources/deployments",
- "apiVersion": "2022-09-01",
+ "apiVersion": "2025-04-01",
"name": "[take(format('avm.res.network.network-security-group.{0}.{1}', tryGet(parameters('subnets')[copyIndex()], 'networkSecurityGroup', 'name'), parameters('resourceSuffix')), 64)]",
"properties": {
"expressionEvaluationOptions": {
@@ -6340,7 +6334,7 @@
},
"virtualNetwork": {
"type": "Microsoft.Resources/deployments",
- "apiVersion": "2022-09-01",
+ "apiVersion": "2025-04-01",
"name": "[take(format('avm.res.network.virtual-network.{0}', parameters('name')), 64)]",
"properties": {
"expressionEvaluationOptions": {
@@ -8067,7 +8061,7 @@
"bastionHost": {
"condition": "[parameters('enablePrivateNetworking')]",
"type": "Microsoft.Resources/deployments",
- "apiVersion": "2022-09-01",
+ "apiVersion": "2025-04-01",
"name": "[take(format('avm.res.network.bastion-host.{0}', variables('bastionHostName')), 64)]",
"properties": {
"expressionEvaluationOptions": {
@@ -9386,7 +9380,7 @@
"jumpboxVM": {
"condition": "[parameters('enablePrivateNetworking')]",
"type": "Microsoft.Resources/deployments",
- "apiVersion": "2022-09-01",
+ "apiVersion": "2025-04-01",
"name": "[take(format('avm.res.compute.virtual-machine.{0}', variables('jumpboxVmName')), 64)]",
"properties": {
"expressionEvaluationOptions": {
@@ -17736,7 +17730,7 @@
},
"condition": "[and(parameters('enablePrivateNetworking'), or(empty(parameters('existingFoundryProjectResourceId')), not(contains(variables('aiRelatedDnsZoneIndices'), copyIndex()))))]",
"type": "Microsoft.Resources/deployments",
- "apiVersion": "2022-09-01",
+ "apiVersion": "2025-04-01",
"name": "[format('avm.res.network.private-dns-zone.{0}', split(variables('privateDnsZones')[copyIndex()], '.')[1])]",
"properties": {
"expressionEvaluationOptions": {
@@ -20903,7 +20897,7 @@
},
"keyvault": {
"type": "Microsoft.Resources/deployments",
- "apiVersion": "2022-09-01",
+ "apiVersion": "2025-04-01",
"name": "[take(format('avm.res.key-vault.vault.{0}', variables('keyVaultName')), 64)]",
"properties": {
"expressionEvaluationOptions": {
@@ -20920,9 +20914,7 @@
"tags": {
"value": "[parameters('tags')]"
},
- "sku": {
- "value": "standard"
- },
+ "sku": "[if(parameters('enableScalability'), createObject('value', 'premium'), createObject('value', 'standard'))]",
"publicNetworkAccess": "[if(parameters('enablePrivateNetworking'), createObject('value', 'Disabled'), createObject('value', 'Enabled'))]",
"networkAcls": {
"value": {
@@ -24135,7 +24127,7 @@
"aiFoundryAiServices": {
"condition": "[variables('aiFoundryAIservicesEnabled')]",
"type": "Microsoft.Resources/deployments",
- "apiVersion": "2022-09-01",
+ "apiVersion": "2025-04-01",
"name": "[take(format('avm.res.cognitive-services.account.{0}', variables('aiFoundryAiServicesResourceName')), 64)]",
"properties": {
"expressionEvaluationOptions": {
@@ -24250,8 +24242,8 @@
"metadata": {
"_generator": {
"name": "bicep",
- "version": "0.37.4.10188",
- "templateHash": "11586648700335054863"
+ "version": "0.39.26.7824",
+ "templateHash": "1936381873810101836"
},
"name": "Cognitive Services",
"description": "This module deploys a Cognitive Service."
@@ -25431,7 +25423,7 @@
"cognitive_service_dependencies": {
"condition": "[not(variables('useExistingService'))]",
"type": "Microsoft.Resources/deployments",
- "apiVersion": "2022-09-01",
+ "apiVersion": "2025-04-01",
"name": "[format('cognitive_service_dependencies-{0}', uniqueString('cognitive_service_dependencies', deployment().name))]",
"properties": {
"expressionEvaluationOptions": {
@@ -25483,8 +25475,8 @@
"metadata": {
"_generator": {
"name": "bicep",
- "version": "0.37.4.10188",
- "templateHash": "8352858209491089788"
+ "version": "0.39.26.7824",
+ "templateHash": "17351518472581919759"
}
},
"definitions": {
@@ -26521,7 +26513,7 @@
"count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]"
},
"type": "Microsoft.Resources/deployments",
- "apiVersion": "2022-09-01",
+ "apiVersion": "2025-04-01",
"name": "[format('{0}-cognitiveService-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]",
"subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), resourceGroup().id), '/')[2]]",
"resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), resourceGroup().id), '/')[4]]",
@@ -27272,7 +27264,7 @@
"secretsExport": {
"condition": "[not(equals(parameters('secretsExportConfiguration'), null()))]",
"type": "Microsoft.Resources/deployments",
- "apiVersion": "2022-09-01",
+ "apiVersion": "2025-04-01",
"name": "[format('{0}-secrets-kv', uniqueString(deployment().name, parameters('location')))]",
"subscriptionId": "[split(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '/')[2]]",
"resourceGroup": "[split(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '/')[4]]",
@@ -27296,8 +27288,8 @@
"metadata": {
"_generator": {
"name": "bicep",
- "version": "0.37.4.10188",
- "templateHash": "2491273843075489892"
+ "version": "0.39.26.7824",
+ "templateHash": "4291957610087788581"
}
},
"definitions": {
@@ -27416,7 +27408,7 @@
"aiProject": {
"condition": "[or(not(empty(parameters('projectName'))), not(empty(parameters('existingFoundryProjectResourceId'))))]",
"type": "Microsoft.Resources/deployments",
- "apiVersion": "2022-09-01",
+ "apiVersion": "2025-04-01",
"name": "[take(format('{0}-ai-project-{1}-deployment', parameters('name'), parameters('projectName')), 64)]",
"properties": {
"expressionEvaluationOptions": {
@@ -27450,8 +27442,8 @@
"metadata": {
"_generator": {
"name": "bicep",
- "version": "0.37.4.10188",
- "templateHash": "346451728741152022"
+ "version": "0.39.26.7824",
+ "templateHash": "5108472911734987415"
}
},
"definitions": {
@@ -27630,7 +27622,7 @@
"existing_cognitive_service_dependencies": {
"condition": "[variables('useExistingService')]",
"type": "Microsoft.Resources/deployments",
- "apiVersion": "2022-09-01",
+ "apiVersion": "2025-04-01",
"name": "[format('existing_cognitive_service_dependencies-{0}', uniqueString('existing_cognitive_service_dependencies', deployment().name))]",
"subscriptionId": "[variables('existingCognitiveServiceDetails')[2]]",
"resourceGroup": "[variables('existingCognitiveServiceDetails')[4]]",
@@ -27687,8 +27679,8 @@
"metadata": {
"_generator": {
"name": "bicep",
- "version": "0.37.4.10188",
- "templateHash": "8352858209491089788"
+ "version": "0.39.26.7824",
+ "templateHash": "17351518472581919759"
}
},
"definitions": {
@@ -28725,7 +28717,7 @@
"count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]"
},
"type": "Microsoft.Resources/deployments",
- "apiVersion": "2022-09-01",
+ "apiVersion": "2025-04-01",
"name": "[format('{0}-cognitiveService-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]",
"subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), resourceGroup().id), '/')[2]]",
"resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), resourceGroup().id), '/')[4]]",
@@ -29476,7 +29468,7 @@
"secretsExport": {
"condition": "[not(equals(parameters('secretsExportConfiguration'), null()))]",
"type": "Microsoft.Resources/deployments",
- "apiVersion": "2022-09-01",
+ "apiVersion": "2025-04-01",
"name": "[format('{0}-secrets-kv', uniqueString(deployment().name, parameters('location')))]",
"subscriptionId": "[split(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '/')[2]]",
"resourceGroup": "[split(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '/')[4]]",
@@ -29500,8 +29492,8 @@
"metadata": {
"_generator": {
"name": "bicep",
- "version": "0.37.4.10188",
- "templateHash": "2491273843075489892"
+ "version": "0.39.26.7824",
+ "templateHash": "4291957610087788581"
}
},
"definitions": {
@@ -29620,7 +29612,7 @@
"aiProject": {
"condition": "[or(not(empty(parameters('projectName'))), not(empty(parameters('existingFoundryProjectResourceId'))))]",
"type": "Microsoft.Resources/deployments",
- "apiVersion": "2022-09-01",
+ "apiVersion": "2025-04-01",
"name": "[take(format('{0}-ai-project-{1}-deployment', parameters('name'), parameters('projectName')), 64)]",
"properties": {
"expressionEvaluationOptions": {
@@ -29654,8 +29646,8 @@
"metadata": {
"_generator": {
"name": "bicep",
- "version": "0.37.4.10188",
- "templateHash": "346451728741152022"
+ "version": "0.39.26.7824",
+ "templateHash": "5108472911734987415"
}
},
"definitions": {
@@ -29912,8 +29904,8 @@
}
},
"dependsOn": [
- "[format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').aiServices)]",
"[format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').cognitiveServices)]",
+ "[format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').aiServices)]",
"logAnalyticsWorkspace",
"userAssignedIdentity",
"virtualNetwork"
@@ -29921,7 +29913,7 @@
},
"cosmosDb": {
"type": "Microsoft.Resources/deployments",
- "apiVersion": "2022-09-01",
+ "apiVersion": "2025-04-01",
"name": "[take(format('avm.res.document-db.database-account.{0}', variables('cosmosDbResourceName')), 64)]",
"properties": {
"expressionEvaluationOptions": {
@@ -29984,7 +29976,7 @@
"zoneRedundant": "[if(parameters('enableRedundancy'), createObject('value', true()), createObject('value', false()))]",
"capabilitiesToAdd": "[if(parameters('enableRedundancy'), createObject('value', null()), createObject('value', createArray('EnableServerless')))]",
"automaticFailover": "[if(parameters('enableRedundancy'), createObject('value', true()), createObject('value', false()))]",
- "failoverLocations": "[if(parameters('enableRedundancy'), createObject('value', createArray(createObject('failoverPriority', 0, 'isZoneRedundant', true(), 'locationName', variables('solutionLocation')), createObject('failoverPriority', 1, 'isZoneRedundant', true(), 'locationName', variables('cosmosDbHaLocation')))), createObject('value', createArray(createObject('locationName', variables('solutionLocation'), 'failoverPriority', 0, 'isZoneRedundant', parameters('enableRedundancy')))))]"
+ "failoverLocations": "[if(parameters('enableRedundancy'), createObject('value', createArray(createObject('failoverPriority', 0, 'isZoneRedundant', true(), 'locationName', parameters('cosmosLocation')), createObject('failoverPriority', 1, 'isZoneRedundant', true(), 'locationName', parameters('secondaryCosmosLocation')))), createObject('value', createArray(createObject('locationName', variables('solutionLocation'), 'failoverPriority', 0, 'isZoneRedundant', parameters('enableRedundancy')))))]"
},
"template": {
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
@@ -33761,7 +33753,7 @@
},
"avmStorageAccount": {
"type": "Microsoft.Resources/deployments",
- "apiVersion": "2022-09-01",
+ "apiVersion": "2025-04-01",
"name": "[take(format('avm.res.storage.storage-account.{0}', variables('storageAccountName')), 64)]",
"properties": {
"expressionEvaluationOptions": {
@@ -39531,7 +39523,7 @@
},
"saveStorageAccountSecretsInKeyVault": {
"type": "Microsoft.Resources/deployments",
- "apiVersion": "2022-09-01",
+ "apiVersion": "2025-04-01",
"name": "[take(format('saveStorageAccountSecretsInKeyVault.{0}', variables('keyVaultName')), 64)]",
"properties": {
"expressionEvaluationOptions": {
@@ -39575,7 +39567,7 @@
},
{
"name": "ADLS-ACCOUNT-KEY",
- "value": "[listOutputsWithSecureValues('avmStorageAccount', '2022-09-01').primaryAccessKey]"
+ "value": "[listOutputsWithSecureValues('avmStorageAccount', '2025-04-01').primaryAccessKey]"
}
]
}
@@ -42703,7 +42695,7 @@
},
"sqlDBModule": {
"type": "Microsoft.Resources/deployments",
- "apiVersion": "2022-09-01",
+ "apiVersion": "2025-04-01",
"name": "[take(format('avm.res.sql.server.{0}', variables('sqlDbName')), 64)]",
"properties": {
"expressionEvaluationOptions": {
@@ -42760,7 +42752,6 @@
"primaryUserAssignedIdentityResourceId": {
"value": "[reference('userAssignedIdentity').outputs.resourceId.value]"
},
- "privateEndpoints": "[if(parameters('enablePrivateNetworking'), createObject('value', createArray(createObject('privateDnsZoneGroup', createObject('privateDnsZoneGroupConfigs', createArray(createObject('privateDnsZoneResourceId', reference(format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').sqlServer)).outputs.resourceId.value))), 'service', 'sqlServer', 'subnetResourceId', reference('virtualNetwork').outputs.pepsSubnetResourceId.value, 'tags', parameters('tags')))), createObject('value', createArray()))]",
"firewallRules": "[if(not(parameters('enablePrivateNetworking')), createObject('value', createArray(createObject('endIpAddress', '255.255.255.255', 'name', 'AllowSpecificRange', 'startIpAddress', '0.0.0.0'), createObject('endIpAddress', '0.0.0.0', 'name', 'AllowAllWindowsAzureIps', 'startIpAddress', '0.0.0.0'))), createObject('value', createArray()))]",
"tags": {
"value": "[parameters('tags')]"
@@ -49337,16 +49328,15 @@
}
},
"dependsOn": [
- "[format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').sqlServer)]",
"logAnalyticsWorkspace",
- "userAssignedIdentity",
- "virtualNetwork"
+ "userAssignedIdentity"
]
},
- "webServerFarm": {
+ "sqlDbPrivateEndpoint": {
+ "condition": "[parameters('enablePrivateNetworking')]",
"type": "Microsoft.Resources/deployments",
- "apiVersion": "2022-09-01",
- "name": "[take(format('avm.res.web.serverfarm.{0}', variables('webServerFarmResourceName')), 64)]",
+ "apiVersion": "2025-04-01",
+ "name": "[take(format('avm.res.network.private-endpoint.sql-{0}', variables('solutionSuffix')), 64)]",
"properties": {
"expressionEvaluationOptions": {
"scope": "inner"
@@ -49354,7 +49344,10 @@
"mode": "Incremental",
"parameters": {
"name": {
- "value": "[variables('webServerFarmResourceName')]"
+ "value": "[format('pep-sql-{0}', variables('solutionSuffix'))]"
+ },
+ "location": {
+ "value": "[variables('solutionLocation')]"
},
"tags": {
"value": "[parameters('tags')]"
@@ -49362,21 +49355,34 @@
"enableTelemetry": {
"value": "[parameters('enableTelemetry')]"
},
- "location": {
- "value": "[variables('solutionLocation')]"
- },
- "reserved": {
- "value": true
+ "subnetResourceId": {
+ "value": "[reference('virtualNetwork').outputs.pepsSubnetResourceId.value]"
},
- "kind": {
- "value": "linux"
+ "customNetworkInterfaceName": {
+ "value": "[format('nic-sql-{0}', variables('solutionSuffix'))]"
},
- "diagnosticSettings": "[if(parameters('enableMonitoring'), createObject('value', createArray(createObject('workspaceResourceId', if(variables('useExistingLogAnalytics'), parameters('existingLogAnalyticsWorkspaceId'), reference('logAnalyticsWorkspace').outputs.resourceId.value)))), createObject('value', null()))]",
- "skuName": "[if(or(parameters('enableScalability'), parameters('enableRedundancy')), createObject('value', 'P1v3'), createObject('value', 'B3'))]",
- "skuCapacity": {
- "value": 1
+ "privateLinkServiceConnections": {
+ "value": [
+ {
+ "name": "[format('pl-sqlserver-{0}', variables('solutionSuffix'))]",
+ "properties": {
+ "privateLinkServiceId": "[reference('sqlDBModule').outputs.resourceId.value]",
+ "groupIds": [
+ "sqlServer"
+ ]
+ }
+ }
+ ]
},
- "zoneRedundant": "[if(parameters('enableRedundancy'), createObject('value', true()), createObject('value', false()))]"
+ "privateDnsZoneGroup": {
+ "value": {
+ "privateDnsZoneGroupConfigs": [
+ {
+ "privateDnsZoneResourceId": "[reference(format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').sqlServer)).outputs.resourceId.value]"
+ }
+ ]
+ }
+ }
},
"template": {
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
@@ -49385,100 +49391,36 @@
"metadata": {
"_generator": {
"name": "bicep",
- "version": "0.36.177.2456",
- "templateHash": "16945786131371363466"
+ "version": "0.38.5.1644",
+ "templateHash": "16604612898799598358"
},
- "name": "App Service Plan",
- "description": "This module deploys an App Service Plan."
+ "name": "Private Endpoints",
+ "description": "This module deploys a Private Endpoint."
},
"definitions": {
- "diagnosticSettingMetricsOnlyType": {
+ "privateDnsZoneGroupType": {
"type": "object",
"properties": {
"name": {
"type": "string",
"nullable": true,
"metadata": {
- "description": "Optional. The name of diagnostic setting."
+ "description": "Optional. The name of the Private DNS Zone Group."
}
},
- "metricCategories": {
+ "privateDnsZoneGroupConfigs": {
"type": "array",
"items": {
- "type": "object",
- "properties": {
- "category": {
- "type": "string",
- "metadata": {
- "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics."
- }
- },
- "enabled": {
- "type": "bool",
- "nullable": true,
- "metadata": {
- "description": "Optional. Enable or disable the category explicitly. Default is `true`."
- }
- }
- }
+ "$ref": "#/definitions/privateDnsZoneGroupConfigType"
},
- "nullable": true,
- "metadata": {
- "description": "Optional. The name of metrics that will be streamed. \"allMetrics\" includes all possible metrics for the resource. Set to `[]` to disable metric collection."
- }
- },
- "logAnalyticsDestinationType": {
- "type": "string",
- "allowedValues": [
- "AzureDiagnostics",
- "Dedicated"
- ],
- "nullable": true,
- "metadata": {
- "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type."
- }
- },
- "workspaceResourceId": {
- "type": "string",
- "nullable": true,
- "metadata": {
- "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub."
- }
- },
- "storageAccountResourceId": {
- "type": "string",
- "nullable": true,
- "metadata": {
- "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub."
- }
- },
- "eventHubAuthorizationRuleResourceId": {
- "type": "string",
- "nullable": true,
- "metadata": {
- "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to."
- }
- },
- "eventHubName": {
- "type": "string",
- "nullable": true,
- "metadata": {
- "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub."
- }
- },
- "marketplacePartnerResourceId": {
- "type": "string",
- "nullable": true,
"metadata": {
- "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs."
+ "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones."
}
}
},
"metadata": {
- "description": "An AVM-aligned type for a diagnostic setting. To be used if only metrics are supported by the resource provider.",
- "__bicep_imported_from!": {
- "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.6.0"
- }
+ "__bicep_export!": true,
+ "description": "The type of a private dns zone group."
}
},
"lockType": {
@@ -49514,7 +49456,31 @@
"metadata": {
"description": "An AVM-aligned type for a lock.",
"__bicep_imported_from!": {
- "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.6.0"
+ "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.6.1"
+ }
+ }
+ },
+ "privateDnsZoneGroupConfigType": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The name of the private DNS zone group config."
+ }
+ },
+ "privateDnsZoneResourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The resource id of the private DNS zone."
+ }
+ }
+ },
+ "metadata": {
+ "description": "The type of a private DNS zone group configuration.",
+ "__bicep_imported_from!": {
+ "sourceTemplate": "private-dns-zone-group/main.bicep"
}
}
},
@@ -49589,7 +49555,7 @@
"metadata": {
"description": "An AVM-aligned type for a role assignment.",
"__bicep_imported_from!": {
- "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.6.0"
+ "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.6.1"
}
}
}
@@ -49597,114 +49563,800 @@
"parameters": {
"name": {
"type": "string",
- "minLength": 1,
- "maxLength": 60,
"metadata": {
- "description": "Required. Name of the app service plan."
+ "description": "Required. Name of the private endpoint resource to create."
}
},
- "skuName": {
+ "subnetResourceId": {
"type": "string",
- "defaultValue": "P1v3",
"metadata": {
- "example": " 'F1'\n 'B1'\n 'P1v3'\n 'I1v2'\n 'FC1'\n ",
- "description": "Optional. The name of the SKU will Determine the tier, size, family of the App Service Plan. This defaults to P1v3 to leverage availability zones."
+ "description": "Required. Resource ID of the subnet where the endpoint needs to be created."
}
},
- "skuCapacity": {
- "type": "int",
- "defaultValue": 3,
- "metadata": {
- "description": "Optional. Number of workers associated with the App Service Plan. This defaults to 3, to leverage availability zones."
- }
- },
- "location": {
- "type": "string",
- "defaultValue": "[resourceGroup().location]",
+ "applicationSecurityGroupResourceIds": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "nullable": true,
"metadata": {
- "description": "Optional. Location for all resources."
+ "description": "Optional. Application security groups in which the private endpoint IP configuration is included."
}
},
- "kind": {
+ "customNetworkInterfaceName": {
"type": "string",
- "defaultValue": "app",
- "allowedValues": [
- "app",
- "elastic",
- "functionapp",
- "windows",
- "linux"
- ],
+ "nullable": true,
"metadata": {
- "description": "Optional. Kind of server OS."
+ "description": "Optional. The custom name of the network interface attached to the private endpoint."
}
},
- "reserved": {
- "type": "bool",
- "defaultValue": "[equals(parameters('kind'), 'linux')]",
+ "ipConfigurations": {
+ "type": "array",
"metadata": {
- "description": "Conditional. Defaults to false when creating Windows/app App Service Plan. Required if creating a Linux App Service Plan and must be set to true."
- }
+ "__bicep_resource_derived_type!": {
+ "source": "Microsoft.Network/privateEndpoints@2024-01-01#properties/properties/properties/ipConfigurations"
+ },
+ "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints."
+ },
+ "nullable": true
},
- "appServiceEnvironmentResourceId": {
- "type": "string",
- "defaultValue": "",
+ "privateDnsZoneGroup": {
+ "$ref": "#/definitions/privateDnsZoneGroupType",
+ "nullable": true,
"metadata": {
- "description": "Optional. The Resource ID of the App Service Environment to use for the App Service Plan."
+ "description": "Optional. The private DNS zone group to configure for the private endpoint."
}
},
- "workerTierName": {
+ "location": {
"type": "string",
- "defaultValue": "",
- "metadata": {
- "description": "Optional. Target worker tier assigned to the App Service plan."
- }
- },
- "perSiteScaling": {
- "type": "bool",
- "defaultValue": false,
- "metadata": {
- "description": "Optional. If true, apps assigned to this App Service plan can be scaled independently. If false, apps assigned to this App Service plan will scale to all instances of the plan."
- }
- },
- "elasticScaleEnabled": {
- "type": "bool",
- "defaultValue": "[greater(parameters('maximumElasticWorkerCount'), 1)]",
- "metadata": {
- "description": "Optional. Enable/Disable ElasticScaleEnabled App Service Plan."
- }
- },
- "maximumElasticWorkerCount": {
- "type": "int",
- "defaultValue": 1,
- "metadata": {
- "description": "Optional. Maximum number of total workers allowed for this ElasticScaleEnabled App Service Plan."
- }
- },
- "targetWorkerCount": {
- "type": "int",
- "defaultValue": 0,
- "metadata": {
- "description": "Optional. Scaling worker count."
- }
- },
- "targetWorkerSize": {
- "type": "int",
- "defaultValue": 0,
- "allowedValues": [
- 0,
- 1,
- 2
- ],
- "metadata": {
- "description": "Optional. The instance size of the hosting plan (small, medium, or large)."
- }
- },
- "zoneRedundant": {
- "type": "bool",
- "defaultValue": "[if(or(startsWith(parameters('skuName'), 'P'), startsWith(parameters('skuName'), 'EP')), true(), false())]",
+ "defaultValue": "[resourceGroup().location]",
"metadata": {
- "description": "Optional. Zone Redundant server farms can only be used on Premium or ElasticPremium SKU tiers within ZRS Supported regions (https://learn.microsoft.com/en-us/azure/storage/common/redundancy-regions-zrs)."
+ "description": "Optional. Location for all Resources."
+ }
+ },
+ "lock": {
+ "$ref": "#/definitions/lockType",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The lock settings of the service."
+ }
+ },
+ "roleAssignments": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/roleAssignmentType"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Array of role assignments to create."
+ }
+ },
+ "tags": {
+ "type": "object",
+ "metadata": {
+ "__bicep_resource_derived_type!": {
+ "source": "Microsoft.Network/privateEndpoints@2024-01-01#properties/tags"
+ },
+ "description": "Optional. Tags to be applied on all resources/resource groups in this deployment."
+ },
+ "nullable": true
+ },
+ "customDnsConfigs": {
+ "type": "array",
+ "metadata": {
+ "__bicep_resource_derived_type!": {
+ "source": "Microsoft.Network/privateEndpoints@2024-01-01#properties/properties/properties/customDnsConfigs"
+ },
+ "description": "Optional. Custom DNS configurations."
+ },
+ "nullable": true
+ },
+ "manualPrivateLinkServiceConnections": {
+ "type": "array",
+ "metadata": {
+ "__bicep_resource_derived_type!": {
+ "source": "Microsoft.Network/privateEndpoints@2024-01-01#properties/properties/properties/manualPrivateLinkServiceConnections"
+ },
+ "description": "Conditional. A grouping of information about the connection to the remote resource. Used when the network admin does not have access to approve connections to the remote resource. Required if `privateLinkServiceConnections` is empty."
+ },
+ "nullable": true
+ },
+ "privateLinkServiceConnections": {
+ "type": "array",
+ "metadata": {
+ "__bicep_resource_derived_type!": {
+ "source": "Microsoft.Network/privateEndpoints@2024-01-01#properties/properties/properties/privateLinkServiceConnections"
+ },
+ "description": "Conditional. A grouping of information about the connection to the remote resource. Required if `manualPrivateLinkServiceConnections` is empty."
+ },
+ "nullable": true
+ },
+ "enableTelemetry": {
+ "type": "bool",
+ "defaultValue": true,
+ "metadata": {
+ "description": "Optional. Enable/Disable usage telemetry for module."
+ }
+ }
+ },
+ "variables": {
+ "copy": [
+ {
+ "name": "formattedRoleAssignments",
+ "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]",
+ "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]"
+ }
+ ],
+ "builtInRoleNames": {
+ "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]",
+ "DNS Resolver Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d')]",
+ "DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314')]",
+ "Domain Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2')]",
+ "Domain Services Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb')]",
+ "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]",
+ "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]",
+ "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]",
+ "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]",
+ "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]"
+ }
+ },
+ "resources": {
+ "avmTelemetry": {
+ "condition": "[parameters('enableTelemetry')]",
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2025-04-01",
+ "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.11.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]",
+ "properties": {
+ "mode": "Incremental",
+ "template": {
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "contentVersion": "1.0.0.0",
+ "resources": [],
+ "outputs": {
+ "telemetry": {
+ "type": "String",
+ "value": "For more information, see https://aka.ms/avm/TelemetryInfo"
+ }
+ }
+ }
+ }
+ },
+ "privateEndpoint": {
+ "type": "Microsoft.Network/privateEndpoints",
+ "apiVersion": "2024-10-01",
+ "name": "[parameters('name')]",
+ "location": "[parameters('location')]",
+ "tags": "[parameters('tags')]",
+ "properties": {
+ "copy": [
+ {
+ "name": "applicationSecurityGroups",
+ "count": "[length(coalesce(parameters('applicationSecurityGroupResourceIds'), createArray()))]",
+ "input": {
+ "id": "[coalesce(parameters('applicationSecurityGroupResourceIds'), createArray())[copyIndex('applicationSecurityGroups')]]"
+ }
+ }
+ ],
+ "customDnsConfigs": "[coalesce(parameters('customDnsConfigs'), createArray())]",
+ "customNetworkInterfaceName": "[coalesce(parameters('customNetworkInterfaceName'), '')]",
+ "ipConfigurations": "[coalesce(parameters('ipConfigurations'), createArray())]",
+ "manualPrivateLinkServiceConnections": "[coalesce(parameters('manualPrivateLinkServiceConnections'), createArray())]",
+ "privateLinkServiceConnections": "[coalesce(parameters('privateLinkServiceConnections'), createArray())]",
+ "subnet": {
+ "id": "[parameters('subnetResourceId')]"
+ }
+ }
+ },
+ "privateEndpoint_lock": {
+ "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]",
+ "type": "Microsoft.Authorization/locks",
+ "apiVersion": "2020-05-01",
+ "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]",
+ "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]",
+ "properties": {
+ "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]",
+ "notes": "[coalesce(tryGet(parameters('lock'), 'notes'), if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.'))]"
+ },
+ "dependsOn": [
+ "privateEndpoint"
+ ]
+ },
+ "privateEndpoint_roleAssignments": {
+ "copy": {
+ "name": "privateEndpoint_roleAssignments",
+ "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]"
+ },
+ "type": "Microsoft.Authorization/roleAssignments",
+ "apiVersion": "2022-04-01",
+ "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]",
+ "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateEndpoints', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]",
+ "properties": {
+ "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]",
+ "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]",
+ "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]",
+ "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]",
+ "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]",
+ "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]",
+ "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]"
+ },
+ "dependsOn": [
+ "privateEndpoint"
+ ]
+ },
+ "privateEndpoint_privateDnsZoneGroup": {
+ "condition": "[not(empty(parameters('privateDnsZoneGroup')))]",
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2025-04-01",
+ "name": "[format('{0}-PrivateEndpoint-PrivateDnsZoneGroup', uniqueString(deployment().name))]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "name": {
+ "value": "[tryGet(parameters('privateDnsZoneGroup'), 'name')]"
+ },
+ "privateEndpointName": {
+ "value": "[parameters('name')]"
+ },
+ "privateDnsZoneConfigs": {
+ "value": "[parameters('privateDnsZoneGroup').privateDnsZoneGroupConfigs]"
+ }
+ },
+ "template": {
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "languageVersion": "2.0",
+ "contentVersion": "1.0.0.0",
+ "metadata": {
+ "_generator": {
+ "name": "bicep",
+ "version": "0.38.5.1644",
+ "templateHash": "24141742673128945"
+ },
+ "name": "Private Endpoint Private DNS Zone Groups",
+ "description": "This module deploys a Private Endpoint Private DNS Zone Group."
+ },
+ "definitions": {
+ "privateDnsZoneGroupConfigType": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The name of the private DNS zone group config."
+ }
+ },
+ "privateDnsZoneResourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The resource id of the private DNS zone."
+ }
+ }
+ },
+ "metadata": {
+ "__bicep_export!": true,
+ "description": "The type of a private DNS zone group configuration."
+ }
+ }
+ },
+ "parameters": {
+ "privateEndpointName": {
+ "type": "string",
+ "metadata": {
+ "description": "Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment."
+ }
+ },
+ "privateDnsZoneConfigs": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/privateDnsZoneGroupConfigType"
+ },
+ "minLength": 1,
+ "maxLength": 5,
+ "metadata": {
+ "description": "Required. Array of private DNS zone configurations of the private DNS zone group. A DNS zone group can support up to 5 DNS zones."
+ }
+ },
+ "name": {
+ "type": "string",
+ "defaultValue": "default",
+ "metadata": {
+ "description": "Optional. The name of the private DNS zone group."
+ }
+ }
+ },
+ "resources": {
+ "privateEndpoint": {
+ "existing": true,
+ "type": "Microsoft.Network/privateEndpoints",
+ "apiVersion": "2024-10-01",
+ "name": "[parameters('privateEndpointName')]"
+ },
+ "privateDnsZoneGroup": {
+ "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups",
+ "apiVersion": "2024-10-01",
+ "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]",
+ "properties": {
+ "copy": [
+ {
+ "name": "privateDnsZoneConfigs",
+ "count": "[length(parameters('privateDnsZoneConfigs'))]",
+ "input": {
+ "name": "[coalesce(tryGet(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigs')], 'name'), last(split(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigs')].privateDnsZoneResourceId, '/')))]",
+ "properties": {
+ "privateDnsZoneId": "[parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigs')].privateDnsZoneResourceId]"
+ }
+ }
+ }
+ ]
+ }
+ }
+ },
+ "outputs": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the private endpoint DNS zone group."
+ },
+ "value": "[parameters('name')]"
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the private endpoint DNS zone group."
+ },
+ "value": "[resourceId('Microsoft.Network/privateEndpoints/privateDnsZoneGroups', parameters('privateEndpointName'), parameters('name'))]"
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource group the private endpoint DNS zone group was deployed into."
+ },
+ "value": "[resourceGroup().name]"
+ }
+ }
+ }
+ },
+ "dependsOn": [
+ "privateEndpoint"
+ ]
+ }
+ },
+ "outputs": {
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource group the private endpoint was deployed into."
+ },
+ "value": "[resourceGroup().name]"
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the private endpoint."
+ },
+ "value": "[resourceId('Microsoft.Network/privateEndpoints', parameters('name'))]"
+ },
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the private endpoint."
+ },
+ "value": "[parameters('name')]"
+ },
+ "location": {
+ "type": "string",
+ "metadata": {
+ "description": "The location the resource was deployed into."
+ },
+ "value": "[reference('privateEndpoint', '2024-10-01', 'full').location]"
+ },
+ "customDnsConfigs": {
+ "type": "array",
+ "metadata": {
+ "__bicep_resource_derived_type!": {
+ "source": "Microsoft.Network/privateEndpoints@2024-01-01#properties/properties/properties/customDnsConfigs",
+ "output": true
+ },
+ "description": "The custom DNS configurations of the private endpoint."
+ },
+ "value": "[reference('privateEndpoint').customDnsConfigs]"
+ },
+ "networkInterfaceResourceIds": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "metadata": {
+ "description": "The resource IDs of the network interfaces associated with the private endpoint."
+ },
+ "value": "[map(reference('privateEndpoint').networkInterfaces, lambda('nic', lambdaVariables('nic').id))]"
+ },
+ "groupId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "The group Id for the private endpoint Group."
+ },
+ "value": "[coalesce(tryGet(tryGet(tryGet(tryGet(reference('privateEndpoint'), 'manualPrivateLinkServiceConnections'), 0, 'properties'), 'groupIds'), 0), tryGet(tryGet(tryGet(tryGet(reference('privateEndpoint'), 'privateLinkServiceConnections'), 0, 'properties'), 'groupIds'), 0))]"
+ }
+ }
+ }
+ },
+ "dependsOn": [
+ "[format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').sqlServer)]",
+ "sqlDBModule",
+ "virtualNetwork"
+ ]
+ },
+ "webServerFarm": {
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2025-04-01",
+ "name": "[take(format('avm.res.web.serverfarm.{0}', variables('webServerFarmResourceName')), 64)]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "name": {
+ "value": "[variables('webServerFarmResourceName')]"
+ },
+ "tags": {
+ "value": "[parameters('tags')]"
+ },
+ "enableTelemetry": {
+ "value": "[parameters('enableTelemetry')]"
+ },
+ "location": {
+ "value": "[variables('solutionLocation')]"
+ },
+ "reserved": {
+ "value": true
+ },
+ "kind": {
+ "value": "linux"
+ },
+ "diagnosticSettings": "[if(parameters('enableMonitoring'), createObject('value', createArray(createObject('workspaceResourceId', if(variables('useExistingLogAnalytics'), parameters('existingLogAnalyticsWorkspaceId'), reference('logAnalyticsWorkspace').outputs.resourceId.value)))), createObject('value', null()))]",
+ "skuName": {
+ "value": "B3"
+ },
+ "skuCapacity": {
+ "value": 1
+ },
+ "zoneRedundant": {
+ "value": false
+ }
+ },
+ "template": {
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "languageVersion": "2.0",
+ "contentVersion": "1.0.0.0",
+ "metadata": {
+ "_generator": {
+ "name": "bicep",
+ "version": "0.36.177.2456",
+ "templateHash": "16945786131371363466"
+ },
+ "name": "App Service Plan",
+ "description": "This module deploys an App Service Plan."
+ },
+ "definitions": {
+ "diagnosticSettingMetricsOnlyType": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The name of diagnostic setting."
+ }
+ },
+ "metricCategories": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "category": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics."
+ }
+ },
+ "enabled": {
+ "type": "bool",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Enable or disable the category explicitly. Default is `true`."
+ }
+ }
+ }
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The name of metrics that will be streamed. \"allMetrics\" includes all possible metrics for the resource. Set to `[]` to disable metric collection."
+ }
+ },
+ "logAnalyticsDestinationType": {
+ "type": "string",
+ "allowedValues": [
+ "AzureDiagnostics",
+ "Dedicated"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type."
+ }
+ },
+ "workspaceResourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub."
+ }
+ },
+ "storageAccountResourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub."
+ }
+ },
+ "eventHubAuthorizationRuleResourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to."
+ }
+ },
+ "eventHubName": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub."
+ }
+ },
+ "marketplacePartnerResourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs."
+ }
+ }
+ },
+ "metadata": {
+ "description": "An AVM-aligned type for a diagnostic setting. To be used if only metrics are supported by the resource provider.",
+ "__bicep_imported_from!": {
+ "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.6.0"
+ }
+ }
+ },
+ "lockType": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specify the name of lock."
+ }
+ },
+ "kind": {
+ "type": "string",
+ "allowedValues": [
+ "CanNotDelete",
+ "None",
+ "ReadOnly"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specify the type of lock."
+ }
+ },
+ "notes": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specify the notes of the lock."
+ }
+ }
+ },
+ "metadata": {
+ "description": "An AVM-aligned type for a lock.",
+ "__bicep_imported_from!": {
+ "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.6.0"
+ }
+ }
+ },
+ "roleAssignmentType": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated."
+ }
+ },
+ "roleDefinitionIdOrName": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'."
+ }
+ },
+ "principalId": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to."
+ }
+ },
+ "principalType": {
+ "type": "string",
+ "allowedValues": [
+ "Device",
+ "ForeignGroup",
+ "Group",
+ "ServicePrincipal",
+ "User"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The principal type of the assigned principal ID."
+ }
+ },
+ "description": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The description of the role assignment."
+ }
+ },
+ "condition": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"."
+ }
+ },
+ "conditionVersion": {
+ "type": "string",
+ "allowedValues": [
+ "2.0"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Version of the condition."
+ }
+ },
+ "delegatedManagedIdentityResourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The Resource Id of the delegated managed identity resource."
+ }
+ }
+ },
+ "metadata": {
+ "description": "An AVM-aligned type for a role assignment.",
+ "__bicep_imported_from!": {
+ "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.6.0"
+ }
+ }
+ }
+ },
+ "parameters": {
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 60,
+ "metadata": {
+ "description": "Required. Name of the app service plan."
+ }
+ },
+ "skuName": {
+ "type": "string",
+ "defaultValue": "P1v3",
+ "metadata": {
+ "example": " 'F1'\n 'B1'\n 'P1v3'\n 'I1v2'\n 'FC1'\n ",
+ "description": "Optional. The name of the SKU will Determine the tier, size, family of the App Service Plan. This defaults to P1v3 to leverage availability zones."
+ }
+ },
+ "skuCapacity": {
+ "type": "int",
+ "defaultValue": 3,
+ "metadata": {
+ "description": "Optional. Number of workers associated with the App Service Plan. This defaults to 3, to leverage availability zones."
+ }
+ },
+ "location": {
+ "type": "string",
+ "defaultValue": "[resourceGroup().location]",
+ "metadata": {
+ "description": "Optional. Location for all resources."
+ }
+ },
+ "kind": {
+ "type": "string",
+ "defaultValue": "app",
+ "allowedValues": [
+ "app",
+ "elastic",
+ "functionapp",
+ "windows",
+ "linux"
+ ],
+ "metadata": {
+ "description": "Optional. Kind of server OS."
+ }
+ },
+ "reserved": {
+ "type": "bool",
+ "defaultValue": "[equals(parameters('kind'), 'linux')]",
+ "metadata": {
+ "description": "Conditional. Defaults to false when creating Windows/app App Service Plan. Required if creating a Linux App Service Plan and must be set to true."
+ }
+ },
+ "appServiceEnvironmentResourceId": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "Optional. The Resource ID of the App Service Environment to use for the App Service Plan."
+ }
+ },
+ "workerTierName": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "Optional. Target worker tier assigned to the App Service plan."
+ }
+ },
+ "perSiteScaling": {
+ "type": "bool",
+ "defaultValue": false,
+ "metadata": {
+ "description": "Optional. If true, apps assigned to this App Service plan can be scaled independently. If false, apps assigned to this App Service plan will scale to all instances of the plan."
+ }
+ },
+ "elasticScaleEnabled": {
+ "type": "bool",
+ "defaultValue": "[greater(parameters('maximumElasticWorkerCount'), 1)]",
+ "metadata": {
+ "description": "Optional. Enable/Disable ElasticScaleEnabled App Service Plan."
+ }
+ },
+ "maximumElasticWorkerCount": {
+ "type": "int",
+ "defaultValue": 1,
+ "metadata": {
+ "description": "Optional. Maximum number of total workers allowed for this ElasticScaleEnabled App Service Plan."
+ }
+ },
+ "targetWorkerCount": {
+ "type": "int",
+ "defaultValue": 0,
+ "metadata": {
+ "description": "Optional. Scaling worker count."
+ }
+ },
+ "targetWorkerSize": {
+ "type": "int",
+ "defaultValue": 0,
+ "allowedValues": [
+ 0,
+ 1,
+ 2
+ ],
+ "metadata": {
+ "description": "Optional. The instance size of the hosting plan (small, medium, or large)."
+ }
+ },
+ "zoneRedundant": {
+ "type": "bool",
+ "defaultValue": "[if(or(startsWith(parameters('skuName'), 'P'), startsWith(parameters('skuName'), 'EP')), true(), false())]",
+ "metadata": {
+ "description": "Optional. Zone Redundant server farms can only be used on Premium or ElasticPremium SKU tiers within ZRS Supported regions (https://learn.microsoft.com/en-us/azure/storage/common/redundancy-regions-zrs)."
}
},
"lock": {
@@ -49918,7 +50570,7 @@
},
"webSite": {
"type": "Microsoft.Resources/deployments",
- "apiVersion": "2022-09-01",
+ "apiVersion": "2025-04-01",
"name": "[take(format('module.web-sites.{0}', variables('webSiteResourceName')), 64)]",
"properties": {
"expressionEvaluationOptions": {
@@ -50030,8 +50682,8 @@
"metadata": {
"_generator": {
"name": "bicep",
- "version": "0.37.4.10188",
- "templateHash": "4298119334635398540"
+ "version": "0.39.26.7824",
+ "templateHash": "13074777962389399773"
}
},
"definitions": {
@@ -51008,7 +51660,7 @@
"count": "[length(coalesce(parameters('configs'), createArray()))]"
},
"type": "Microsoft.Resources/deployments",
- "apiVersion": "2022-09-01",
+ "apiVersion": "2025-04-01",
"name": "[format('{0}-Site-Config-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]",
"properties": {
"expressionEvaluationOptions": {
@@ -51043,8 +51695,8 @@
"metadata": {
"_generator": {
"name": "bicep",
- "version": "0.37.4.10188",
- "templateHash": "4653685834544796273"
+ "version": "0.39.26.7824",
+ "templateHash": "11666262061409473778"
},
"name": "Site App Settings",
"description": "This module deploys a Site App Setting."
@@ -51189,7 +51841,7 @@
"count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]"
},
"type": "Microsoft.Resources/deployments",
- "apiVersion": "2022-09-01",
+ "apiVersion": "2025-04-01",
"name": "[format('{0}-app-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]",
"subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), resourceGroup().id), '/')[2]]",
"resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), resourceGroup().id), '/')[4]]",
@@ -52015,7 +52667,7 @@
},
"searchService": {
"type": "Microsoft.Resources/deployments",
- "apiVersion": "2022-09-01",
+ "apiVersion": "2025-04-01",
"name": "[take(format('avm.res.search.search-service.{0}', variables('aiSearchName')), 64)]",
"properties": {
"expressionEvaluationOptions": {
@@ -52081,9 +52733,7 @@
"replicaCount": {
"value": 1
},
- "sku": {
- "value": "standard"
- },
+ "sku": "[if(parameters('enableScalability'), createObject('value', 'standard'), createObject('value', 'basic'))]",
"semanticSearch": {
"value": "free"
},
@@ -54395,7 +55045,7 @@
"existing_AIProject_SearchConnectionModule": {
"condition": "[variables('useExistingAiFoundryAiProject')]",
"type": "Microsoft.Resources/deployments",
- "apiVersion": "2022-09-01",
+ "apiVersion": "2025-04-01",
"name": "aiProjectSearchConnectionDeployment",
"subscriptionId": "[variables('aiFoundryAiServicesSubscriptionId')]",
"resourceGroup": "[variables('aiFoundryAiServicesResourceGroupName')]",
@@ -54430,8 +55080,8 @@
"metadata": {
"_generator": {
"name": "bicep",
- "version": "0.37.4.10188",
- "templateHash": "6038840175458269917"
+ "version": "0.39.26.7824",
+ "templateHash": "904007681755275486"
}
},
"parameters": {
@@ -54499,7 +55149,7 @@
"searchServiceToExistingAiServicesRoleAssignment": {
"condition": "[variables('useExistingAiFoundryAiProject')]",
"type": "Microsoft.Resources/deployments",
- "apiVersion": "2022-09-01",
+ "apiVersion": "2025-04-01",
"name": "searchToExistingAiServices-roleAssignment",
"subscriptionId": "[variables('aiFoundryAiServicesSubscriptionId')]",
"resourceGroup": "[variables('aiFoundryAiServicesResourceGroupName')]",
@@ -54525,8 +55175,8 @@
"metadata": {
"_generator": {
"name": "bicep",
- "version": "0.37.4.10188",
- "templateHash": "3644919950024112374"
+ "version": "0.39.26.7824",
+ "templateHash": "10276790018915749779"
}
},
"parameters": {
diff --git a/infra/main.parameters.json b/infra/main.parameters.json
index de31ba4b9..f50195da0 100644
--- a/infra/main.parameters.json
+++ b/infra/main.parameters.json
@@ -8,6 +8,9 @@
"cosmosLocation": {
"value": "${AZURE_ENV_COSMOS_LOCATION}"
},
+ "secondaryCosmosLocation": {
+ "value": "${AZURE_ENV_COSMOS_SECONDARY_LOCATION}"
+ },
"gptModelDeploymentType": {
"value": "${AZURE_ENV_MODEL_DEPLOYMENT_TYPE}"
},
@@ -32,6 +35,9 @@
"location": {
"value": "${AZURE_LOCATION}"
},
+ "azureAiServiceLocation": {
+ "value": "${AZURE_ENV_AI_SERVICE_LOCATION}"
+ },
"existingLogAnalyticsWorkspaceId": {
"value": "${AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID}"
},
diff --git a/infra/main.waf.parameters.json b/infra/main.waf.parameters.json
index 236212679..caff016d5 100644
--- a/infra/main.waf.parameters.json
+++ b/infra/main.waf.parameters.json
@@ -8,6 +8,9 @@
"cosmosLocation": {
"value": "${AZURE_ENV_COSMOS_LOCATION}"
},
+ "secondaryCosmosLocation": {
+ "value": "${AZURE_ENV_COSMOS_SECONDARY_LOCATION}"
+ },
"gptModelDeploymentType": {
"value": "${AZURE_ENV_MODEL_DEPLOYMENT_TYPE}"
},
@@ -32,6 +35,9 @@
"location": {
"value": "${AZURE_LOCATION}"
},
+ "azureAiServiceLocation": {
+ "value": "${AZURE_ENV_AI_SERVICE_LOCATION}"
+ },
"existingLogAnalyticsWorkspaceId": {
"value": "${AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID}"
},