From d56b105f8a215afe7ef78755e14507c2e66c5887 Mon Sep 17 00:00:00 2001 From: Seth Date: Thu, 24 Apr 2025 15:23:29 -0400 Subject: [PATCH 01/44] Quota check - documentation and bash script. updated main readme to include this prereq --- README.md | 12 +- docs/quota_check.md | 99 +++++++++ img/Documentation/quota-check-output.png | Bin 0 -> 12857 bytes scripts/quota_check.sh | 250 +++++++++++++++++++++++ 4 files changed, 356 insertions(+), 5 deletions(-) create mode 100644 docs/quota_check.md create mode 100644 img/Documentation/quota-check-output.png create mode 100644 scripts/quota_check.sh diff --git a/README.md b/README.md index 9c1d05d..104fe6f 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,12 @@ The diagram below illustrates the capabilities included in the template. 6. If leveraging [One-click deployment](#quick-deploy). 7. If leveraging [GitHub Actions](docs/github_actions_steps.md). +### Check Azure OpenAI Quota Availability + +To ensure sufficient quota is available in your subscription, please follow **[quota check instructions guide](./docs/quota_check.md)** before deploying the solution. + +### Services Enabled + For additional documentation of the default enabled services of this solution accelerator, please see: 1. [Azure Open AI Service](https://learn.microsoft.com/en-us/azure/ai-services/openai/) @@ -89,7 +95,7 @@ Follow the post deployment steps [Post Deployment Steps](docs/github_code_spaces ### Region Availability -By default, this template uses AI models which may not be available in all Azure regions. Check for [up-to-date region availability](https://learn.microsoft.com/azure/ai-services/openai/concepts/models#standard-deployment-model-availability) and select a region during deployment accordingly. +By default, this template uses AI models which may not be available in all Azure regions. Please follow [quota check instructions guide](./docs/quota_check.md) before deploying the solution. Additionally, check for [up-to-date region availability](https://learn.microsoft.com/azure/ai-services/openai/concepts/models#standard-deployment-model-availability) and select a region during deployment accordingly. ### Costs @@ -99,16 +105,12 @@ You can estimate the cost of this project's architecture with [Azure's pricing c This template has [Managed Identity](https://learn.microsoft.com/entra/identity/managed-identities-azure-resources/overview) built in to eliminate the need for developers to manage these credentials. Applications can use managed identities to obtain Microsoft Entra tokens without having to manage any credentials. - ## Resources - [Azure AI Foundry documentation](https://learn.microsoft.com/en-us/azure/ai-studio/) - [Azure Well Architecture Framework documentation](https://learn.microsoft.com/en-us/azure/well-architected/) - [Azure OpenAI Service - Documentation, quickstarts, API reference - Azure AI services | Microsoft Learn](https://learn.microsoft.com/en-us/azure/ai-services/openai/concepts/use-your-data) - [Azure AI Content Understanding documentation](https://learn.microsoft.com/en-us/azure/ai-services/content-understanding/) - - - --- ## Disclaimers diff --git a/docs/quota_check.md b/docs/quota_check.md new file mode 100644 index 0000000..83cf2df --- /dev/null +++ b/docs/quota_check.md @@ -0,0 +1,99 @@ +# Check Quota Availability Before Deployment + +Before deploying the accelerator, **ensure sufficient quota availability** for the required model. +> **We recommend increasing the capacity to 100k tokens for optimal performance.** + +## Login if you have not done so already +``` +azd auth login +``` + +## 📌 Default Models & Capacities: +``` +gpt-4o:30, gpt-4o-mini:30, gpt-4:30, text-embedding-ada-002:80 +``` +## 📌 Default Regions: +``` +eastus, uksouth, eastus2, northcentralus, swedencentral, westus, westus2, southcentralus, canadacentral +``` +## Usage Scenarios: +- No parameters passed → Default models and capacities will be checked in default regions. +- Only model(s) provided → The script will check for those models in the default regions. +- Only region(s) provided → The script will check default models in the specified regions. +- Both models and regions provided → The script will check those models in the specified regions. +- `--verbose` passed → Enables detailed logging output for debugging and traceability. + +## **Input Formats** +> Use the --models, --regions, and --verbose options for parameter handling: + +✔️ Run without parameters to check default models & regions without verbose logging: + ``` + ./quota_check.sh + ``` +✔️ Enable verbose logging: + ``` + ./quota_check.sh --verbose + ``` +✔️ Check specific model(s) in default regions: + ``` + ./quota_check.sh --models gpt-4o:30,text-embedding-ada-002:80 + ``` +✔️ Check default models in specific region(s): + ``` +./quota_check.sh --regions eastus,westus + ``` +✔️ Passing Both models and regions: + ``` + ./quota_check.sh --models gpt-4o:30 --regions eastus,westus2 + ``` +✔️ All parameters combined: + ``` + ./quota_check.sh --models gpt-4:30,text-embedding-ada-002:80 --regions eastus,westus --verbose + ``` + +## **Sample Output** +The final table lists regions with available quota. You can select any of these regions for deployment. + +![quota-check-ouput](../img/Documentation/quota-check-output.png) + +--- +## **If using Azure Portal and Cloud Shell** + +1. Navigate to the [Azure Portal](https://portal.azure.com). +2. Click on **Azure Cloud Shell** in the top right navigation menu. +3. Run the appropriate command based on your requirement: + + **To check quota for the deployment** + + ```sh + curl -L -o quota_check.sh "https://raw.githubusercontent.com/microsoft/Deploy-Your-AI-Application-In-Production/main/scripts/quota_check.sh" + chmod +x quota_check.sh + ./quota_check.sh + ``` + - Refer to [Input Formats](#input-formats) for detailed commands. + +## **If using VS Code or Codespaces** +1. Open the terminal in VS Code or Codespaces. +2. If you're using VS Code, click the dropdown on the right side of the terminal window, and select `Git Bash`. + ![git_bash](Images/git_bash.png) +3. Navigate to the `scripts` folder where the script files are located and make the script as executable: + ```sh + cd scripts + chmod +x quota_check.sh + ``` +4. Run the appropriate script based on your requirement: + + **To check quota for the deployment** + + ```sh + ./quota_check.sh + ``` + - Refer to [Input Formats](#input-formats) for detailed commands. + +5. If you see the error `_bash: az: command not found_`, install Azure CLI: + + ```sh + curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash + az login + ``` +6. Rerun the script after installing Azure CLI. \ No newline at end of file diff --git a/img/Documentation/quota-check-output.png b/img/Documentation/quota-check-output.png new file mode 100644 index 0000000000000000000000000000000000000000..9c80e329886cb9619eef2a49e493d9535e9482c7 GIT binary patch literal 12857 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 literal 0 HcmV?d00001 diff --git a/scripts/quota_check.sh b/scripts/quota_check.sh new file mode 100644 index 0000000..6929bd7 --- /dev/null +++ b/scripts/quota_check.sh @@ -0,0 +1,250 @@ +#!/bin/bash +# VERBOSE=false + +MODELS="" +REGIONS="" +VERBOSE=false + +while [[ $# -gt 0 ]]; do + case "$1" in + --models) + MODELS="$2" + shift 2 + ;; + --regions) + REGIONS="$2" + shift 2 + ;; + --verbose) + VERBOSE=true + shift + ;; + *) + echo "Unknown option: $1" + exit 1 + ;; + esac +done + +# Fallback to defaults if not provided +[[ -z "$MODELS" ]] +[[ -z "$REGIONS" ]] + +echo "Models: $MODELS" +echo "Regions: $REGIONS" +echo "Verbose: $VERBOSE" + +for arg in "$@"; do + if [ "$arg" = "--verbose" ]; then + VERBOSE=true + fi +done + +log_verbose() { + if [ "$VERBOSE" = true ]; then + echo "$1" + fi +} + +# Default Models and Capacities (Comma-separated in "model:capacity" format) +DEFAULT_MODEL_CAPACITY="gpt-4o:30,gpt-4o-mini:30,gpt-4:30,text-embedding-ada-002:80" + +# Convert the comma-separated string into an array +IFS=',' read -r -a MODEL_CAPACITY_PAIRS <<< "$DEFAULT_MODEL_CAPACITY" + +echo "🔄 Fetching available Azure subscriptions..." +SUBSCRIPTIONS=$(az account list --query "[?state=='Enabled'].{Name:name, ID:id}" --output tsv) +SUB_COUNT=$(echo "$SUBSCRIPTIONS" | wc -l) + +if [ "$SUB_COUNT" -eq 0 ]; then + echo "❌ ERROR: No active Azure subscriptions found. Please log in using 'az login' and ensure you have an active subscription." + exit 1 +elif [ "$SUB_COUNT" -eq 1 ]; then + # If only one subscription, automatically select it + AZURE_SUBSCRIPTION_ID=$(echo "$SUBSCRIPTIONS" | awk '{print $2}') + if [ -z "$AZURE_SUBSCRIPTION_ID" ]; then + echo "❌ ERROR: No active Azure subscriptions found. Please log in using 'az login' and ensure you have an active subscription." + exit 1 + fi + echo "✅ Using the only available subscription: $AZURE_SUBSCRIPTION_ID" +else + # If multiple subscriptions exist, prompt the user to choose one + echo "Multiple subscriptions found:" + echo "$SUBSCRIPTIONS" | awk '{print NR")", $1, "-", $2}' + + while true; do + echo "Enter the number of the subscription to use:" + read SUB_INDEX + + # Validate user input + if [[ "$SUB_INDEX" =~ ^[0-9]+$ ]] && [ "$SUB_INDEX" -ge 1 ] && [ "$SUB_INDEX" -le "$SUB_COUNT" ]; then + AZURE_SUBSCRIPTION_ID=$(echo "$SUBSCRIPTIONS" | awk -v idx="$SUB_INDEX" 'NR==idx {print $2}') + echo "✅ Selected Subscription: $AZURE_SUBSCRIPTION_ID" + break + else + echo "❌ Invalid selection. Please enter a valid number from the list." + fi + done +fi + + +# Set the selected subscription +az account set --subscription "$AZURE_SUBSCRIPTION_ID" +echo "🎯 Active Subscription: $(az account show --query '[name, id]' --output tsv)" + +# Default Regions to check (Comma-separated, now configurable) +DEFAULT_REGIONS="eastus,uksouth,eastus2,northcentralus,swedencentral,westus,westus2,southcentralus,canadacentral" +IFS=',' read -r -a DEFAULT_REGION_ARRAY <<< "$DEFAULT_REGIONS" + +# Read parameters (if any) +IFS=',' read -r -a USER_PROVIDED_PAIRS <<< "$MODELS" +USER_REGION="$REGIONS" + +IS_USER_PROVIDED_PAIRS=false + +if [ ${#USER_PROVIDED_PAIRS[@]} -lt 1 ]; then + echo "No parameters provided, using default model-capacity pairs: ${MODEL_CAPACITY_PAIRS[*]}" +else + echo "Using provided model and capacity pairs: ${USER_PROVIDED_PAIRS[*]}" + IS_USER_PROVIDED_PAIRS=true + MODEL_CAPACITY_PAIRS=("${USER_PROVIDED_PAIRS[@]}") +fi + +declare -a FINAL_MODEL_NAMES +declare -a FINAL_CAPACITIES +declare -a TABLE_ROWS + +for PAIR in "${MODEL_CAPACITY_PAIRS[@]}"; do + MODEL_NAME=$(echo "$PAIR" | cut -d':' -f1 | tr '[:upper:]' '[:lower:]') + CAPACITY=$(echo "$PAIR" | cut -d':' -f2) + + if [ -z "$MODEL_NAME" ] || [ -z "$CAPACITY" ]; then + echo "❌ ERROR: Invalid model and capacity pair '$PAIR'. Both model and capacity must be specified." + exit 1 + fi + + FINAL_MODEL_NAMES+=("$MODEL_NAME") + FINAL_CAPACITIES+=("$CAPACITY") + +done + +echo "🔄 Using Models: ${FINAL_MODEL_NAMES[*]} with respective Capacities: ${FINAL_CAPACITIES[*]}" +echo "----------------------------------------" + +# Check if the user provided a region, if not, use the default regions +if [ -n "$USER_REGION" ]; then + echo "🔍 User provided region: $USER_REGION" + IFS=',' read -r -a REGIONS <<< "$USER_REGION" +else + echo "No region specified, using default regions: ${DEFAULT_REGION_ARRAY[*]}" + REGIONS=("${DEFAULT_REGION_ARRAY[@]}") + APPLY_OR_CONDITION=true +fi + +echo "✅ Retrieved Azure regions. Checking availability..." +INDEX=1 + +VALID_REGIONS=() +for REGION in "${REGIONS[@]}"; do + log_verbose "----------------------------------------" + log_verbose "🔍 Checking region: $REGION" + + QUOTA_INFO=$(az cognitiveservices usage list --location "$REGION" --output json | tr '[:upper:]' '[:lower:]') + if [ -z "$QUOTA_INFO" ]; then + log_verbose "⚠️ WARNING: Failed to retrieve quota for region $REGION. Skipping." + continue + fi + + TEXT_EMBEDDING_AVAILABLE=false + AT_LEAST_ONE_MODEL_AVAILABLE=false + TEMP_TABLE_ROWS=() + + for index in "${!FINAL_MODEL_NAMES[@]}"; do + MODEL_NAME="${FINAL_MODEL_NAMES[$index]}" + REQUIRED_CAPACITY="${FINAL_CAPACITIES[$index]}" + FOUND=false + INSUFFICIENT_QUOTA=false + + if [ "$MODEL_NAME" = "text-embedding-ada-002" ]; then + MODEL_TYPES=("openai.standard.$MODEL_NAME") + else + MODEL_TYPES=("openai.standard.$MODEL_NAME" "openai.globalstandard.$MODEL_NAME") + fi + + for MODEL_TYPE in "${MODEL_TYPES[@]}"; do + FOUND=false + INSUFFICIENT_QUOTA=false + log_verbose "🔍 Checking model: $MODEL_NAME with required capacity: $REQUIRED_CAPACITY ($MODEL_TYPE)" + + MODEL_INFO=$(echo "$QUOTA_INFO" | awk -v model="\"value\": \"$MODEL_TYPE\"" ' + BEGIN { RS="},"; FS="," } + $0 ~ model { print $0 } + ') + + if [ -z "$MODEL_INFO" ]; then + FOUND=false + log_verbose "⚠️ WARNING: No quota information found for model: $MODEL_NAME in region: $REGION for model type: $MODEL_TYPE." + continue + fi + + if [ -n "$MODEL_INFO" ]; then + FOUND=true + CURRENT_VALUE=$(echo "$MODEL_INFO" | awk -F': ' '/"currentvalue"/ {print $2}' | tr -d ',' | tr -d ' ') + LIMIT=$(echo "$MODEL_INFO" | awk -F': ' '/"limit"/ {print $2}' | tr -d ',' | tr -d ' ') + + CURRENT_VALUE=${CURRENT_VALUE:-0} + LIMIT=${LIMIT:-0} + + CURRENT_VALUE=$(echo "$CURRENT_VALUE" | cut -d'.' -f1) + LIMIT=$(echo "$LIMIT" | cut -d'.' -f1) + + AVAILABLE=$((LIMIT - CURRENT_VALUE)) + log_verbose "✅ Model: $MODEL_TYPE | Used: $CURRENT_VALUE | Limit: $LIMIT | Available: $AVAILABLE" + + if [ "$AVAILABLE" -ge "$REQUIRED_CAPACITY" ]; then + FOUND=true + if [ "$MODEL_NAME" = "text-embedding-ada-002" ]; then + TEXT_EMBEDDING_AVAILABLE=true + fi + AT_LEAST_ONE_MODEL_AVAILABLE=true + TEMP_TABLE_ROWS+=("$(printf "| %-4s | %-20s | %-43s | %-10s | %-10s | %-10s |" "$INDEX" "$REGION" "$MODEL_TYPE" "$LIMIT" "$CURRENT_VALUE" "$AVAILABLE")") + else + INSUFFICIENT_QUOTA=true + fi + fi + + if [ "$FOUND" = false ]; then + log_verbose "❌ No models found for model: $MODEL_NAME in region: $REGION (${MODEL_TYPES[*]})" + + elif [ "$INSUFFICIENT_QUOTA" = true ]; then + log_verbose "⚠️ Model $MODEL_NAME in region: $REGION has insufficient quota (${MODEL_TYPES[*]})." + fi + done + done + +if { [ "$IS_USER_PROVIDED_PAIRS" = true ] && [ "$INSUFFICIENT_QUOTA" = false ] && [ "$FOUND" = true ]; } || { [ "$TEXT_EMBEDDING_AVAILABLE" = true ] && { [ "$APPLY_OR_CONDITION" != true ] || [ "$AT_LEAST_ONE_MODEL_AVAILABLE" = true ]; }; }; then + VALID_REGIONS+=("$REGION") + TABLE_ROWS+=("${TEMP_TABLE_ROWS[@]}") + INDEX=$((INDEX + 1)) + elif [ ${#USER_PROVIDED_PAIRS[@]} -eq 0 ]; then + echo "🚫 Skipping $REGION as it does not meet quota requirements." + fi + +done + +if [ ${#TABLE_ROWS[@]} -eq 0 ]; then + echo "--------------------------------------------------------------------------------------------------------------------" + + echo "❌ No regions have sufficient quota for all required models. Please request a quota increase: https://aka.ms/oai/stuquotarequest" +else + echo "---------------------------------------------------------------------------------------------------------------------" + printf "| %-4s | %-20s | %-43s | %-10s | %-10s | %-10s |\n" "No." "Region" "Model Name" "Limit" "Used" "Available" + echo "---------------------------------------------------------------------------------------------------------------------" + for ROW in "${TABLE_ROWS[@]}"; do + echo "$ROW" + done + echo "---------------------------------------------------------------------------------------------------------------------" + echo "➡️ To request a quota increase, visit: https://aka.ms/oai/stuquotarequest" +fi + +echo "✅ Script completed." \ No newline at end of file From 1df653a322f5b07d0eb0253f0bc65eb627bb40de Mon Sep 17 00:00:00 2001 From: Seth Steenken Date: Thu, 24 Apr 2025 15:26:49 -0400 Subject: [PATCH 02/44] Update docs/quota_check.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- docs/quota_check.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/quota_check.md b/docs/quota_check.md index 83cf2df..d22e870 100644 --- a/docs/quota_check.md +++ b/docs/quota_check.md @@ -54,7 +54,7 @@ eastus, uksouth, eastus2, northcentralus, swedencentral, westus, westus2, southc ## **Sample Output** The final table lists regions with available quota. You can select any of these regions for deployment. -![quota-check-ouput](../img/Documentation/quota-check-output.png) +![quota-check-output](../img/Documentation/quota-check-output.png) --- ## **If using Azure Portal and Cloud Shell** From 177747cae977f892c0064db25fb9506d3d213d3c Mon Sep 17 00:00:00 2001 From: Seth Date: Wed, 30 Apr 2025 12:40:00 -0400 Subject: [PATCH 03/44] Quota - initial auto validate model quota script --- scripts/validate_model_deployable.sh | 123 +++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 scripts/validate_model_deployable.sh diff --git a/scripts/validate_model_deployable.sh b/scripts/validate_model_deployable.sh new file mode 100644 index 0000000..9a98d52 --- /dev/null +++ b/scripts/validate_model_deployable.sh @@ -0,0 +1,123 @@ +#!/bin/bash + +SUBSCRIPTION_ID="" +REGION="" +MODEL="" +DEPLOYMENT_TYPE="standard" +CAPACITY=0 + +while [[ $# -gt 0 ]]; do + case "$1" in + --subscription) + SUBSCRIPTION_ID="$2" + shift 2 + ;; + --model) + MODEL="$2" + shift 2 + ;; + --capacity) + CAPACITY="$2" + shift 2 + ;; + --deployment-type) + DEPLOYMENT_TYPE="$2" + shift 2 + ;; + --region) + REGION="$2" + shift 2 + ;; + *) + echo "Unknown option: $1" + exit 1 + ;; + esac +done + +# Verify all required parameters are provided and echo missing ones +MISSING_PARAMS=() + +if [[ -z "$SUBSCRIPTION_ID" ]]; then + MISSING_PARAMS+=("subscription") +fi + +if [[ -z "$REGION" ]]; then + MISSING_PARAMS+=("region") +fi + +if [[ -z "$MODEL" ]]; then + MISSING_PARAMS+=("model") +fi + +if [[ -z "$CAPACITY" ]]; then + MISSING_PARAMS+=("capacity") +fi + +if [[ -z "$DEPLOYMENT_TYPE" ]]; then + MISSING_PARAMS+=("deployment-type") +fi + +if [[ ${#MISSING_PARAMS[@]} -ne 0 ]]; then + echo "❌ ERROR: Missing required parameters: ${MISSING_PARAMS[*]}" + echo "Usage: $0 --subscription --region --model --capacity [--deployment-type ]" + + exit 1 +fi + +if [[ "$DEPLOYMENT_TYPE" != "standard" && "$DEPLOYMENT_TYPE" != "globalstandard" ]]; then + echo "❌ ERROR: Invalid deployment type: $DEPLOYMENT_TYPE. Allowed values are 'standard' or 'globalstandard'." + exit 1 + + if [[ "$MODEL" == text-embedding* && "$DEPLOYMENT_TYPE" != "standard" ]]; then + echo "❌ ERROR: Invalid deployment type: $DEPLOYMENT_TYPE. Value must be 'standard' for embedding models ($MODEL)." + exit 1 + fi +fi + +MODEL_TYPE="openai.$DEPLOYMENT_TYPE.$MODEL" + +# Set the subscription +az account set --subscription "$AZURE_SUBSCRIPTION_ID" +echo "🎯 Active Subscription: $(az account show --query '[name, id]' --output tsv)" + +echo "🔍 Checking region: $REGION" + +QUOTA_INFO=$(az cognitiveservices usage list --location "$REGION" --output json | tr '[:upper:]' '[:lower:]') +if [ -z "$QUOTA_INFO" ]; then + echo "❌ ERROR: Failed to retrieve quota for region $REGION." + exit 1 +fi + +echo "🔍 Checking model: $MODEL ($MODEL_TYPE) with requested capacity: $CAPACITY" + +MODEL_INFO=$(echo "$QUOTA_INFO" | awk -v model="\"value\": \"$MODEL_TYPE\"" ' + BEGIN { RS="},"; FS="," } + $0 ~ model { print $0 } +') + +if [ -z "$MODEL_INFO" ]; then + echo "❌ ERROR: No quota information found for model: $MODEL in region: $REGION for model type: $MODEL_TYPE." + exit 1 +fi + +if [ -n "$MODEL_INFO" ]; then + CURRENT_VALUE=$(echo "$MODEL_INFO" | awk -F': ' '/"currentvalue"/ {print $2}' | tr -d ',' | tr -d ' ') + LIMIT=$(echo "$MODEL_INFO" | awk -F': ' '/"limit"/ {print $2}' | tr -d ',' | tr -d ' ') + + CURRENT_VALUE=${CURRENT_VALUE:-0} + LIMIT=${LIMIT:-0} + + CURRENT_VALUE=$(echo "$CURRENT_VALUE" | cut -d'.' -f1) + LIMIT=$(echo "$LIMIT" | cut -d'.' -f1) + + AVAILABLE=$((LIMIT - CURRENT_VALUE)) + echo "✅ Model available - Model: $MODEL_TYPE | Used: $CURRENT_VALUE | Limit: $LIMIT | Available: $AVAILABLE" + + if [ "$AVAILABLE" -lt "$CAPACITY" ]; then + echo "❌ ERROR: Insufficient quota for model: $MODEL in region: $REGION. Available: $AVAILABLE, Requested: $CAPACITY." + exit 1 + else + echo "✅ Sufficient quota for model: $MODEL in region: $REGION. Available: $AVAILABLE, Requested: $CAPACITY." + fi +fi From 0b4d4d7e81888ce9fa2e1f2411bf44265bc8ecbe Mon Sep 17 00:00:00 2001 From: Mike Swantek <46489667+mswantek68@users.noreply.github.com> Date: Wed, 30 Apr 2025 17:36:23 -0400 Subject: [PATCH 04/44] Update CHANGELOG.md --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f2c534..e4f04e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ All notable changes to this project will be documented in this file. -## [Unreleased] +## [1.1] - 2025-04-30 ### Added - Added feature to collect and connect existing connections from existing project when creating a new isolated 'production' project. - Added Change Log @@ -19,4 +19,4 @@ All notable changes to this project will be documented in this file. ## [1.0] - 2025-03-10 ### Added -- Initial release of the template. \ No newline at end of file +- Initial release of the template. From 45b7db385065dd2c5755ea65be7a9a9383857895 Mon Sep 17 00:00:00 2001 From: Seth Date: Thu, 8 May 2025 10:44:35 -0400 Subject: [PATCH 05/44] Quota check - added bash script for automated quota validation. added preprovision step --- azure.yaml | 8 +- scripts/validate_model_deployable.sh | 123 -------------------- scripts/validate_model_deployment_quotas.sh | 84 +++++++++++++ scripts/validate_model_quota.sh | 93 +++++++++++++++ 4 files changed, 184 insertions(+), 124 deletions(-) delete mode 100644 scripts/validate_model_deployable.sh create mode 100644 scripts/validate_model_deployment_quotas.sh create mode 100644 scripts/validate_model_quota.sh diff --git a/azure.yaml b/azure.yaml index f0baa4b..ab94a4a 100644 --- a/azure.yaml +++ b/azure.yaml @@ -2,4 +2,10 @@ name: deploy-your-ai-application-in-production infra: provider: "bicep" metadata: - template: deploy-your-ai-application-in-production@1.0 \ No newline at end of file + template: deploy-your-ai-application-in-production@1.0 +hooks: + preprovision: + shell: sh + run: ./scripts/validate_model_deployment_quotas.sh --subscription $AZURE_SUBSCRIPTION_ID --location $AZURE_LOCATION --models-parameter "aiModelDeployments" + interactive: false + continueOnError: false \ No newline at end of file diff --git a/scripts/validate_model_deployable.sh b/scripts/validate_model_deployable.sh deleted file mode 100644 index 9a98d52..0000000 --- a/scripts/validate_model_deployable.sh +++ /dev/null @@ -1,123 +0,0 @@ -#!/bin/bash - -SUBSCRIPTION_ID="" -REGION="" -MODEL="" -DEPLOYMENT_TYPE="standard" -CAPACITY=0 - -while [[ $# -gt 0 ]]; do - case "$1" in - --subscription) - SUBSCRIPTION_ID="$2" - shift 2 - ;; - --model) - MODEL="$2" - shift 2 - ;; - --capacity) - CAPACITY="$2" - shift 2 - ;; - --deployment-type) - DEPLOYMENT_TYPE="$2" - shift 2 - ;; - --region) - REGION="$2" - shift 2 - ;; - *) - echo "Unknown option: $1" - exit 1 - ;; - esac -done - -# Verify all required parameters are provided and echo missing ones -MISSING_PARAMS=() - -if [[ -z "$SUBSCRIPTION_ID" ]]; then - MISSING_PARAMS+=("subscription") -fi - -if [[ -z "$REGION" ]]; then - MISSING_PARAMS+=("region") -fi - -if [[ -z "$MODEL" ]]; then - MISSING_PARAMS+=("model") -fi - -if [[ -z "$CAPACITY" ]]; then - MISSING_PARAMS+=("capacity") -fi - -if [[ -z "$DEPLOYMENT_TYPE" ]]; then - MISSING_PARAMS+=("deployment-type") -fi - -if [[ ${#MISSING_PARAMS[@]} -ne 0 ]]; then - echo "❌ ERROR: Missing required parameters: ${MISSING_PARAMS[*]}" - echo "Usage: $0 --subscription --region --model --capacity [--deployment-type ]" - - exit 1 -fi - -if [[ "$DEPLOYMENT_TYPE" != "standard" && "$DEPLOYMENT_TYPE" != "globalstandard" ]]; then - echo "❌ ERROR: Invalid deployment type: $DEPLOYMENT_TYPE. Allowed values are 'standard' or 'globalstandard'." - exit 1 - - if [[ "$MODEL" == text-embedding* && "$DEPLOYMENT_TYPE" != "standard" ]]; then - echo "❌ ERROR: Invalid deployment type: $DEPLOYMENT_TYPE. Value must be 'standard' for embedding models ($MODEL)." - exit 1 - fi -fi - -MODEL_TYPE="openai.$DEPLOYMENT_TYPE.$MODEL" - -# Set the subscription -az account set --subscription "$AZURE_SUBSCRIPTION_ID" -echo "🎯 Active Subscription: $(az account show --query '[name, id]' --output tsv)" - -echo "🔍 Checking region: $REGION" - -QUOTA_INFO=$(az cognitiveservices usage list --location "$REGION" --output json | tr '[:upper:]' '[:lower:]') -if [ -z "$QUOTA_INFO" ]; then - echo "❌ ERROR: Failed to retrieve quota for region $REGION." - exit 1 -fi - -echo "🔍 Checking model: $MODEL ($MODEL_TYPE) with requested capacity: $CAPACITY" - -MODEL_INFO=$(echo "$QUOTA_INFO" | awk -v model="\"value\": \"$MODEL_TYPE\"" ' - BEGIN { RS="},"; FS="," } - $0 ~ model { print $0 } -') - -if [ -z "$MODEL_INFO" ]; then - echo "❌ ERROR: No quota information found for model: $MODEL in region: $REGION for model type: $MODEL_TYPE." - exit 1 -fi - -if [ -n "$MODEL_INFO" ]; then - CURRENT_VALUE=$(echo "$MODEL_INFO" | awk -F': ' '/"currentvalue"/ {print $2}' | tr -d ',' | tr -d ' ') - LIMIT=$(echo "$MODEL_INFO" | awk -F': ' '/"limit"/ {print $2}' | tr -d ',' | tr -d ' ') - - CURRENT_VALUE=${CURRENT_VALUE:-0} - LIMIT=${LIMIT:-0} - - CURRENT_VALUE=$(echo "$CURRENT_VALUE" | cut -d'.' -f1) - LIMIT=$(echo "$LIMIT" | cut -d'.' -f1) - - AVAILABLE=$((LIMIT - CURRENT_VALUE)) - echo "✅ Model available - Model: $MODEL_TYPE | Used: $CURRENT_VALUE | Limit: $LIMIT | Available: $AVAILABLE" - - if [ "$AVAILABLE" -lt "$CAPACITY" ]; then - echo "❌ ERROR: Insufficient quota for model: $MODEL in region: $REGION. Available: $AVAILABLE, Requested: $CAPACITY." - exit 1 - else - echo "✅ Sufficient quota for model: $MODEL in region: $REGION. Available: $AVAILABLE, Requested: $CAPACITY." - fi -fi diff --git a/scripts/validate_model_deployment_quotas.sh b/scripts/validate_model_deployment_quotas.sh new file mode 100644 index 0000000..799d749 --- /dev/null +++ b/scripts/validate_model_deployment_quotas.sh @@ -0,0 +1,84 @@ +#!/bin/bash + +SUBSCRIPTION_ID="" +LOCATION="" +MODELS_PARAMETER="" + +while [[ $# -gt 0 ]]; do + case "$1" in + --subscription) + SUBSCRIPTION_ID="$2" + shift 2 + ;; + --location) + LOCATION="$2" + shift 2 + ;; + --models-parameter) + MODELS_PARAMETER="$2" + shift 2 + ;; + *) + echo "Unknown option: $1" + exit 1 + ;; + esac +done + +# Verify all required parameters are provided and echo missing ones +MISSING_PARAMS=() + +if [[ -z "$SUBSCRIPTION_ID" ]]; then + MISSING_PARAMS+=("subscription") +fi + +if [[ -z "$LOCATION" ]]; then + MISSING_PARAMS+=("location") +fi + +if [[ -z "$MODELS_PARAMETER" ]]; then + MISSING_PARAMS+=("models-parameter") +fi + +if [[ ${#MISSING_PARAMS[@]} -ne 0 ]]; then + echo "❌ ERROR: Missing required parameters: ${MISSING_PARAMS[*]}" + echo "Usage: $0 --subscription --location --models-parameter " + exit 1 +fi + +aiModelDeployments=$(jq -c ".parameters.$MODELS_PARAMETER.value[]" ./infra/main.parameters.json) + +if [ $? -ne 0 ]; then + echo "Error: Failed to parse main.parameters.json. Ensure jq is installed and the JSON file is valid." + exit 1 +fi + +az account set --subscription "$SUBSCRIPTION_ID" +echo "🎯 Active Subscription: $(az account show --query '[name, id]' --output tsv)" + +quotaAvailable=true + +while IFS= read -r deployment; do + name=$(echo "$deployment" | jq -r '.name') + model=$(echo "$deployment" | jq -r '.model.name') + type=$(echo "$deployment" | jq -r '.sku.name') + capacity=$(echo "$deployment" | jq -r '.sku.capacity') + + # Call the validate_model_deployable.sh script + echo "🔍 Validating model deployment: $name ..." + ./scripts/validate_model_quota.sh --location "$LOCATION" --model "$model" --capacity $capacity --deployment-type $type + + # Check if the script failed + if [ $? -ne 0 ]; then + echo "❌ ERROR: Quota validation failed for model deployment: $name" + quotaAvailable=false + fi +done <<< "$(echo "$aiModelDeployments")" + +if [ "$quotaAvailable" = false ]; then + echo "❌ ERROR: One or more model deployments failed validation." + exit 1 +else + echo "✅ All model deployments passed quota validation successfully." + exit 0 +fi diff --git a/scripts/validate_model_quota.sh b/scripts/validate_model_quota.sh new file mode 100644 index 0000000..79a662c --- /dev/null +++ b/scripts/validate_model_quota.sh @@ -0,0 +1,93 @@ +#!/bin/bash + +LOCATION="" +MODEL="" +DEPLOYMENT_TYPE="Standard" +CAPACITY=0 + +while [[ $# -gt 0 ]]; do + case "$1" in + --model) + MODEL="$2" + shift 2 + ;; + --capacity) + CAPACITY="$2" + shift 2 + ;; + --deployment-type) + DEPLOYMENT_TYPE="$2" + shift 2 + ;; + --location) + LOCATION="$2" + shift 2 + ;; + *) + echo "Unknown option: $1" + exit 1 + ;; + esac +done + +# Verify all required parameters are provided and echo missing ones +MISSING_PARAMS=() + +if [[ -z "$LOCATION" ]]; then + MISSING_PARAMS+=("location") +fi + +if [[ -z "$MODEL" ]]; then + MISSING_PARAMS+=("model") +fi + +if [[ -z "$CAPACITY" ]]; then + MISSING_PARAMS+=("capacity") +fi + +if [[ -z "$DEPLOYMENT_TYPE" ]]; then + MISSING_PARAMS+=("deployment-type") +fi + +if [[ ${#MISSING_PARAMS[@]} -ne 0 ]]; then + echo "❌ ERROR: Missing required parameters: ${MISSING_PARAMS[*]}" + echo "Usage: $0 --location --model --capacity [--deployment-type ]" + exit 1 +fi + +if [[ "$DEPLOYMENT_TYPE" != "Standard" && "$DEPLOYMENT_TYPE" != "GlobalStandard" ]]; then + echo "❌ ERROR: Invalid deployment type: $DEPLOYMENT_TYPE. Allowed values are 'Standard' or 'GlobalStandard'." + exit 1 +fi + +MODEL_TYPE="OpenAI.$DEPLOYMENT_TYPE.$MODEL" + +echo "🔍 Checking quota for $MODEL_TYPE in $LOCATION ..." + +MODEL_INFO=$(az cognitiveservices usage list --location "$LOCATION" --query "[?name.value=='$MODEL_TYPE']" --output json | tr '[:upper:]' '[:lower:]') + +if [ -z "$MODEL_INFO" ]; then + echo "❌ ERROR: No quota information found for model: $MODEL in location: $LOCATION for model type: $MODEL_TYPE." + exit 1 +fi + +if [ -n "$MODEL_INFO" ]; then + CURRENT_VALUE=$(echo "$MODEL_INFO" | awk -F': ' '/"currentvalue"/ {print $2}' | tr -d ',' | tr -d ' ') + LIMIT=$(echo "$MODEL_INFO" | awk -F': ' '/"limit"/ {print $2}' | tr -d ',' | tr -d ' ') + + CURRENT_VALUE=${CURRENT_VALUE:-0} + LIMIT=${LIMIT:-0} + + CURRENT_VALUE=$(echo "$CURRENT_VALUE" | cut -d'.' -f1) + LIMIT=$(echo "$LIMIT" | cut -d'.' -f1) + + AVAILABLE=$((LIMIT - CURRENT_VALUE)) + echo "✅ Model available - Model: $MODEL_TYPE | Used: $CURRENT_VALUE | Limit: $LIMIT | Available: $AVAILABLE" + + if [ "$AVAILABLE" -lt "$CAPACITY" ]; then + echo "❌ ERROR: Insufficient quota for model: $MODEL in location: $LOCATION. Available: $AVAILABLE, Requested: $CAPACITY." + exit 1 + else + echo "✅ Sufficient quota for model: $MODEL in location: $LOCATION. Available: $AVAILABLE, Requested: $CAPACITY." + fi +fi From 672c617ee6f0961db4c85783fe17230eb5ee0402 Mon Sep 17 00:00:00 2001 From: Seth Date: Thu, 8 May 2025 11:54:30 -0400 Subject: [PATCH 06/44] Quota validation - added pwsh versions to make deploying from windows easier --- azure.yaml | 14 ++-- scripts/validate_model_deployment_quotas.ps1 | 72 ++++++++++++++++++++ scripts/validate_model_quota.ps1 | 66 ++++++++++++++++++ 3 files changed, 148 insertions(+), 4 deletions(-) create mode 100644 scripts/validate_model_deployment_quotas.ps1 create mode 100644 scripts/validate_model_quota.ps1 diff --git a/azure.yaml b/azure.yaml index 63b7962..f90f317 100644 --- a/azure.yaml +++ b/azure.yaml @@ -11,7 +11,13 @@ hooks: interactive: true continueOnError: false preprovision: - shell: sh - run: ./scripts/validate_model_deployment_quotas.sh --subscription $AZURE_SUBSCRIPTION_ID --location $AZURE_LOCATION --models-parameter "aiModelDeployments" - interactive: false - continueOnError: false \ No newline at end of file + posix: + shell: sh + run: ./scripts/validate_model_deployment_quotas.sh --subscription $AZURE_SUBSCRIPTION_ID --location $AZURE_LOCATION --models-parameter "aiModelDeployments" + interactive: false + continueOnError: false + windows: + shell: pwsh + run: ./scripts/validate_model_deployment_quotas.ps1 -Subscription $env:AZURE_SUBSCRIPTION_ID -Location $env:AZURE_LOCATION -ModelsParameter "aiModelDeployments" + interactive: false + continueOnError: false \ No newline at end of file diff --git a/scripts/validate_model_deployment_quotas.ps1 b/scripts/validate_model_deployment_quotas.ps1 new file mode 100644 index 0000000..6e7ea71 --- /dev/null +++ b/scripts/validate_model_deployment_quotas.ps1 @@ -0,0 +1,72 @@ +param ( + [string]$SubscriptionId, + [string]$Location, + [string]$ModelsParameter +) + +# Verify all required parameters are provided +$MissingParams = @() + +if (-not $SubscriptionId) { + $MissingParams += "subscription" +} + +if (-not $Location) { + $MissingParams += "location" +} + +if (-not $ModelsParameter) { + $MissingParams += "models-parameter" +} + +if ($MissingParams.Count -gt 0) { + Write-Error "❌ ERROR: Missing required parameters: $($MissingParams -join ', ')" + Write-Host "Usage: .\validate_model_deployment_quotas.ps1 -SubscriptionId -Location -ModelsParameter " + exit 1 +} + +$JsonContent = Get-Content -Path "./infra/main.parameters.json" -Raw | ConvertFrom-Json + +if (-not $JsonContent) { + Write-Error "❌ ERROR: Failed to parse main.parameters.json. Ensure the JSON file is valid." + exit 1 +} + +$aiModelDeployments = $JsonContent.parameters.$ModelsParameter.value + +if (-not $aiModelDeployments -or -not ($aiModelDeployments -is [System.Collections.IEnumerable])) { + Write-Error "❌ ERROR: The specified property $ModelsParameter does not exist or is not an array." + exit 1 +} + +# Set the Azure subscription +az account set --subscription $SubscriptionId +Write-Host "🎯 Active Subscription: $(az account show --query '[name, id]' --output tsv)" + +$QuotaAvailable = $true + +# Iterate over each deployment in the aiModelDeployments array +foreach ($deployment in $aiModelDeployments) { + $name = $deployment.name + $model = $deployment.model.name + $type = $deployment.sku.name + $capacity = $deployment.sku.capacity + + # Call the validate_model_quota.ps1 script + Write-Host "🔍 Validating model deployment: $name ..." + & .\scripts\validate_model_quota.ps1 -Location $Location -Model $model -Capacity $capacity -DeploymentType $type + + # Check if the script failed + if ($LASTEXITCODE -ne 0) { + Write-Error "❌ ERROR: Quota validation failed for model deployment: $name" + $QuotaAvailable = $false + } +} + +if (-not $QuotaAvailable) { + Write-Error "❌ ERROR: One or more model deployments failed validation." + exit 1 +} else { + Write-Host "✅ All model deployments passed quota validation successfully." + exit 0 +} \ No newline at end of file diff --git a/scripts/validate_model_quota.ps1 b/scripts/validate_model_quota.ps1 new file mode 100644 index 0000000..43995d3 --- /dev/null +++ b/scripts/validate_model_quota.ps1 @@ -0,0 +1,66 @@ +param ( + [string]$Location, + [string]$Model, + [string]$DeploymentType = "Standard", + [int]$Capacity +) + +# Verify all required parameters are provided +$MissingParams = @() + +if (-not $Location) { + $MissingParams += "location" +} + +if (-not $Model) { + $MissingParams += "model" +} + +if (-not $Capacity) { + $MissingParams += "capacity" +} + +if (-not $DeploymentType) { + $MissingParams += "deployment-type" +} + +if ($MissingParams.Count -gt 0) { + Write-Error "❌ ERROR: Missing required parameters: $($MissingParams -join ', ')" + Write-Host "Usage: .\validate_model_quota.ps1 -Location -Model -Capacity [-DeploymentType ]" + exit 1 +} + +if ($DeploymentType -ne "Standard" -and $DeploymentType -ne "GlobalStandard") { + Write-Error "❌ ERROR: Invalid deployment type: $DeploymentType. Allowed values are 'Standard' or 'GlobalStandard'." + exit 1 +} + +$ModelType = "OpenAI.$DeploymentType.$Model" + +Write-Host "🔍 Checking quota for $ModelType in $Location ..." + +# Get quota information using Azure CLI +$ModelInfo = az cognitiveservices usage list --location $Location --query "[?name.value=='$ModelType']" --output json | ConvertFrom-Json + +if (-not $ModelInfo) { + Write-Error "❌ ERROR: No quota information found for model: $Model in location: $Location for model type: $ModelType." + exit 1 +} + +if ($ModelInfo) { + $CurrentValue = ($ModelInfo | Where-Object { $_.name.value -eq $ModelType }).currentValue + $Limit = ($ModelInfo | Where-Object { $_.name.value -eq $ModelType }).limit + + $CurrentValue = [int]($CurrentValue -replace '\.0+$', '') # Remove decimals + $Limit = [int]($Limit -replace '\.0+$', '') # Remove decimals + + $Available = $Limit - $CurrentValue + Write-Host "✅ Model available - Model: $ModelType | Used: $CurrentValue | Limit: $Limit | Available: $Available" + + if ($Available -lt $Capacity) { + Write-Error "❌ ERROR: Insufficient quota for model: $Model in location: $Location. Available: $Available, Requested: $Capacity." + exit 1 + } else { + Write-Host "✅ Sufficient quota for model: $Model in location: $Location. Available: $Available, Requested: $Capacity." + } +} \ No newline at end of file From 9eaa71411a3ff9c09fb21d33386d671660b970f5 Mon Sep 17 00:00:00 2001 From: Seth Date: Thu, 8 May 2025 16:09:11 -0400 Subject: [PATCH 07/44] Quota check - typo, comment clean up --- scripts/validate_model_deployment_quotas.ps1 | 3 --- scripts/validate_model_deployment_quotas.sh | 1 - scripts/validate_model_quota.ps1 | 2 +- 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/scripts/validate_model_deployment_quotas.ps1 b/scripts/validate_model_deployment_quotas.ps1 index 6e7ea71..43e38c2 100644 --- a/scripts/validate_model_deployment_quotas.ps1 +++ b/scripts/validate_model_deployment_quotas.ps1 @@ -39,20 +39,17 @@ if (-not $aiModelDeployments -or -not ($aiModelDeployments -is [System.Collectio exit 1 } -# Set the Azure subscription az account set --subscription $SubscriptionId Write-Host "🎯 Active Subscription: $(az account show --query '[name, id]' --output tsv)" $QuotaAvailable = $true -# Iterate over each deployment in the aiModelDeployments array foreach ($deployment in $aiModelDeployments) { $name = $deployment.name $model = $deployment.model.name $type = $deployment.sku.name $capacity = $deployment.sku.capacity - # Call the validate_model_quota.ps1 script Write-Host "🔍 Validating model deployment: $name ..." & .\scripts\validate_model_quota.ps1 -Location $Location -Model $model -Capacity $capacity -DeploymentType $type diff --git a/scripts/validate_model_deployment_quotas.sh b/scripts/validate_model_deployment_quotas.sh index 799d749..17a5347 100644 --- a/scripts/validate_model_deployment_quotas.sh +++ b/scripts/validate_model_deployment_quotas.sh @@ -64,7 +64,6 @@ while IFS= read -r deployment; do type=$(echo "$deployment" | jq -r '.sku.name') capacity=$(echo "$deployment" | jq -r '.sku.capacity') - # Call the validate_model_deployable.sh script echo "🔍 Validating model deployment: $name ..." ./scripts/validate_model_quota.sh --location "$LOCATION" --model "$model" --capacity $capacity --deployment-type $type diff --git a/scripts/validate_model_quota.ps1 b/scripts/validate_model_quota.ps1 index 43995d3..eda43ae 100644 --- a/scripts/validate_model_quota.ps1 +++ b/scripts/validate_model_quota.ps1 @@ -39,7 +39,7 @@ $ModelType = "OpenAI.$DeploymentType.$Model" Write-Host "🔍 Checking quota for $ModelType in $Location ..." -# Get quota information using Azure CLI +# Get model quota information $ModelInfo = az cognitiveservices usage list --location $Location --query "[?name.value=='$ModelType']" --output json | ConvertFrom-Json if (-not $ModelInfo) { From e805498bb7c28ba6472779b152f09cb060c83c7e Mon Sep 17 00:00:00 2001 From: Mike Swantek <46489667+mswantek68@users.noreply.github.com> Date: Mon, 12 May 2025 11:28:51 -0400 Subject: [PATCH 08/44] added chmod step to allow execute permission --- azure.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/azure.yaml b/azure.yaml index f90f317..0a0ce0a 100644 --- a/azure.yaml +++ b/azure.yaml @@ -13,7 +13,9 @@ hooks: preprovision: posix: shell: sh - run: ./scripts/validate_model_deployment_quotas.sh --subscription $AZURE_SUBSCRIPTION_ID --location $AZURE_LOCATION --models-parameter "aiModelDeployments" + run: | + chmod +x ./scripts/validate_model_deployment_quotas.sh + ./scripts/validate_model_deployment_quotas.sh --subscription $AZURE_SUBSCRIPTION_ID --location $AZURE_LOCATION --models-parameter "aiModelDeployments" interactive: false continueOnError: false windows: From bae8df902f2ffa3464920167cfd03fd41a31a3e3 Mon Sep 17 00:00:00 2001 From: Mike Swantek <46489667+mswantek68@users.noreply.github.com> Date: Mon, 12 May 2025 11:33:47 -0400 Subject: [PATCH 09/44] uncommited previous change --- azure.yaml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/azure.yaml b/azure.yaml index 0a0ce0a..f90f317 100644 --- a/azure.yaml +++ b/azure.yaml @@ -13,9 +13,7 @@ hooks: preprovision: posix: shell: sh - run: | - chmod +x ./scripts/validate_model_deployment_quotas.sh - ./scripts/validate_model_deployment_quotas.sh --subscription $AZURE_SUBSCRIPTION_ID --location $AZURE_LOCATION --models-parameter "aiModelDeployments" + run: ./scripts/validate_model_deployment_quotas.sh --subscription $AZURE_SUBSCRIPTION_ID --location $AZURE_LOCATION --models-parameter "aiModelDeployments" interactive: false continueOnError: false windows: From 5d35e66ebbe8d4488d370e227a656b3fde4b9c7f Mon Sep 17 00:00:00 2001 From: Mike Swantek <46489667+mswantek68@users.noreply.github.com> Date: Mon, 12 May 2025 11:53:31 -0400 Subject: [PATCH 10/44] chmod on sh file --- scripts/validate_model_deployment_quotas.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 scripts/validate_model_deployment_quotas.sh diff --git a/scripts/validate_model_deployment_quotas.sh b/scripts/validate_model_deployment_quotas.sh old mode 100644 new mode 100755 From a8e14704a43e3b1a4e8a42c445dad7dcdf899c43 Mon Sep 17 00:00:00 2001 From: Mike Swantek <46489667+mswantek68@users.noreply.github.com> Date: Mon, 12 May 2025 13:19:06 -0400 Subject: [PATCH 11/44] update the execution policy --- azure.yaml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/azure.yaml b/azure.yaml index f90f317..93a3074 100644 --- a/azure.yaml +++ b/azure.yaml @@ -7,7 +7,9 @@ hooks: preup: windows: shell: pwsh - run: ./scripts/SetConnectionsEnvironmentVariables.ps1 + run: | + Set-ExecutionPolicy -Scope Process -ExecutionPolicy RemoteSigned + ./scripts/SetConnectionsEnvironmentVariables.ps1 interactive: true continueOnError: false preprovision: @@ -18,6 +20,8 @@ hooks: continueOnError: false windows: shell: pwsh - run: ./scripts/validate_model_deployment_quotas.ps1 -Subscription $env:AZURE_SUBSCRIPTION_ID -Location $env:AZURE_LOCATION -ModelsParameter "aiModelDeployments" + run: | + Set-ExecutionPolicy -Scope Process -ExecutionPolicy RemoteSigned + ./scripts/validate_model_deployment_quotas.ps1 -Subscription $env:AZURE_SUBSCRIPTION_ID -Location $env:AZURE_LOCATION -ModelsParameter "aiModelDeployments" interactive: false continueOnError: false \ No newline at end of file From f731968c12366010ead781e2a48e0dab066481b4 Mon Sep 17 00:00:00 2001 From: Mike Swantek <46489667+mswantek68@users.noreply.github.com> Date: Tue, 13 May 2025 09:49:15 -0400 Subject: [PATCH 12/44] REvert previous changes --- azure.yaml | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/azure.yaml b/azure.yaml index 93a3074..f90f317 100644 --- a/azure.yaml +++ b/azure.yaml @@ -7,9 +7,7 @@ hooks: preup: windows: shell: pwsh - run: | - Set-ExecutionPolicy -Scope Process -ExecutionPolicy RemoteSigned - ./scripts/SetConnectionsEnvironmentVariables.ps1 + run: ./scripts/SetConnectionsEnvironmentVariables.ps1 interactive: true continueOnError: false preprovision: @@ -20,8 +18,6 @@ hooks: continueOnError: false windows: shell: pwsh - run: | - Set-ExecutionPolicy -Scope Process -ExecutionPolicy RemoteSigned - ./scripts/validate_model_deployment_quotas.ps1 -Subscription $env:AZURE_SUBSCRIPTION_ID -Location $env:AZURE_LOCATION -ModelsParameter "aiModelDeployments" + run: ./scripts/validate_model_deployment_quotas.ps1 -Subscription $env:AZURE_SUBSCRIPTION_ID -Location $env:AZURE_LOCATION -ModelsParameter "aiModelDeployments" interactive: false continueOnError: false \ No newline at end of file From 01af097e0e0ceabab0263d34abc8b967f479f9c9 Mon Sep 17 00:00:00 2001 From: Seth Date: Wed, 21 May 2025 13:43:43 -0400 Subject: [PATCH 13/44] Quota - script permission update, dev container dependeny update --- .devcontainer/devcontainer.json | 6 +++--- azure.yaml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 745207d..5717fbc 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -4,8 +4,8 @@ "image": "mcr.microsoft.com/devcontainers/python:3.10-bullseye", "features": { // See https://containers.dev/features for list of features - "ghcr.io/devcontainers/features/docker-in-docker:2": { - }, - "ghcr.io/azure/azure-dev/azd:latest": {} + "ghcr.io/devcontainers/features/docker-in-docker:2": {}, + "ghcr.io/azure/azure-dev/azd:latest": {}, + "ghcr.io/devcontainers/features/azure-cli:1": {} } } diff --git a/azure.yaml b/azure.yaml index f90f317..4c4e3e9 100644 --- a/azure.yaml +++ b/azure.yaml @@ -13,7 +13,7 @@ hooks: preprovision: posix: shell: sh - run: ./scripts/validate_model_deployment_quotas.sh --subscription $AZURE_SUBSCRIPTION_ID --location $AZURE_LOCATION --models-parameter "aiModelDeployments" + run: chmod u+r+x ./scripts/validate_model_deployment_quotas.sh; chmod u+r+x ./scripts/validate_model_quota.sh; ./scripts/validate_model_deployment_quotas.sh --subscription $AZURE_SUBSCRIPTION_ID --location $AZURE_LOCATION --models-parameter "aiModelDeployments" interactive: false continueOnError: false windows: From f7a3122076d020b911c223e722a1d2167443e009 Mon Sep 17 00:00:00 2001 From: Seth Date: Wed, 21 May 2025 14:07:55 -0400 Subject: [PATCH 14/44] Azure CLI dependency documentation --- .github/workflows/azure-dev.yml | 9 +++++++-- README.md | 2 +- docs/github_code_spaces_steps.md | 14 ++++++++------ docs/local_environment_steps.md | 10 ++++++++-- docs/quota_check.md | 12 ++---------- 5 files changed, 26 insertions(+), 21 deletions(-) diff --git a/.github/workflows/azure-dev.yml b/.github/workflows/azure-dev.yml index 124693f..7b0cf41 100644 --- a/.github/workflows/azure-dev.yml +++ b/.github/workflows/azure-dev.yml @@ -25,14 +25,19 @@ jobs: uses: actions/checkout@v4 - name: Install azd uses: Azure/setup-azd@v2 - - name: Log in with Azure (Federated Credentials) + - name: Azure Developer CLI Login run: | azd auth login ` --client-id "$Env:AZURE_CLIENT_ID" ` --federated-credential-provider "github" ` --tenant-id "$Env:AZURE_TENANT_ID" shell: pwsh - + - name: Azure CLI Login + uses: azure/login@v2 + with: + client-id: ${{ vars.AZURE_CLIENT_ID }} + tenant-id: ${{ vars.AZURE_TENANT_ID }} + subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }} - name: Provision Infrastructure run: azd provision --no-prompt env: diff --git a/README.md b/README.md index ae7a902..2744e05 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ Offers ability to [start with an existing Azure AI Project](docs/transfer_projec 1. Have access to an Azure subscription and Entra ID account with Contributor permissions. 2. Confirm the subscription you are deploying into has the [Required Roles and Scopes](docs/Required_roles_scopes_resources.md). 3. The solution ensures secure access to the private VNET through a jump-box VM with Azure Bastion. By default, Bastion does not require an inbound NSG rule for network traffic. However, if your environment enforces specific policy rules, you can resolve access issues by entering your machine's IP address in the `allowedIpAddress` parameter when prompted during deployment. If not specified, all IP addresses are allowed to connect to Azure Bastion. -4. If deploying from your [local environment](docs/local_environment_steps.md), install the [Azure Developer CLI (AZD)](https://learn.microsoft.com/en-us/azure/developer/azure-developer-cli/install-azd?tabs=winget-windows%2Cbrew-mac%2Cscript-linux&pivots=os-windows). +4. If deploying from your [local environment](docs/local_environment_steps.md), install the [Azure CLI (AZ)](https://learn.microsoft.com/en-us/cli/azure/install-azure-cli?view=azure-cli-latest) and the [Azure Developer CLI (AZD)](https://learn.microsoft.com/en-us/azure/developer/azure-developer-cli/install-azd?tabs=winget-windows%2Cbrew-mac%2Cscript-linux&pivots=os-windows). 5. If deploying via [GitHub Codespaces](docs/github_code_spaces_steps.md) - requires the user to be on a GitHub Team or Enterprise Cloud plan. 6. If leveraging [One-click deployment](#quick-deploy). 7. If leveraging [GitHub Actions](docs/github_actions_steps.md). diff --git a/docs/github_code_spaces_steps.md b/docs/github_code_spaces_steps.md index e034185..0d2a177 100644 --- a/docs/github_code_spaces_steps.md +++ b/docs/github_code_spaces_steps.md @@ -36,27 +36,29 @@ You can run this solution using GitHub Codespaces. The button will open a web-ba ![Image showing the pop up window in the web browser for azd auth](../img/provisioning/azdauthpopup.png) -7. Return to the codespaces window now. In the terminal window, begin by initializing the environment by typing the command “azd init” +7. Repeat the same process in #6 using the “az login” command. The [Azure CLI](https://learn.microsoft.com/en-us/cli/azure/what-is-azure-cli?view=azure-cli-latest) is used to validate available AI model quota. + +8. Return to the codespaces window now. In the terminal window, begin by initializing the environment by typing the command “azd init” ![image showing the initial screen in the vs code terminal](../img/provisioning/azd_init_terminal.png) -8. Enter the name for your environment +9. Enter the name for your environment ![aImage showing entering a new environment name](../img/provisioning/enter_evn_name.png) -9. Now start the deployment of the infrastructure by typing the command “azd provision” +10. Now start the deployment of the infrastructure by typing the command “azd provision” ![image showing the terminal in vs code](../img/provisioning/azd_provision_terminal.png) - This step will allow you to choose from the subscriptions you have available, based on the account you logged in with in the azd auth login step. Next it will prompt you for the region to deploy the resources into. + This step will allow you to choose from the subscriptions you have available, based on the account you logged in with in the login step. Next it will prompt you for the region to deploy the resources into. ![image showing region selection](../img/provisioning/azdprovision_select_location.png) -10. Next you will be prompted for values to enable additional features outside of the AI Foundry required features. They are false by default. +11. Next you will be prompted for values to enable additional features outside of the AI Foundry required features. They are false by default. ![image of prompts](../img/provisioning/prompts.png) **Be sure to remember the vm password and vm username. This will be used in a later step. Because we are using FDPO subscriptions, we do not have access to Entra to create the SSO to the jump box at this time. You are still required to log into Azure once you connect to the virtual machine. -11. After completeing the required paramters that you were prompted for, the provisioning of resources will run and deploy the Network Isolated AI hub, project and dependent resources in about 20 minutes. +12. After completeing the required paramters that you were prompted for, the provisioning of resources will run and deploy the Network Isolated AI hub, project and dependent resources in about 20 minutes. # Post Deployment Steps: These steps will help to check that the isolated environment was set up correctly. diff --git a/docs/local_environment_steps.md b/docs/local_environment_steps.md index e8283c7..95840f6 100644 --- a/docs/local_environment_steps.md +++ b/docs/local_environment_steps.md @@ -7,9 +7,9 @@ git clone https://github.com/microsoft/Deploy-Your-AI-Application-In-Production. cd Deploy-Your-AI-Application-In-Production ``` -### Establish AZD Environment +### Establish Environment -This solution uses the [Azure Developer CLI](https://learn.microsoft.com/en-us/azure/developer/azure-developer-cli/overview) to quickly provision and deploy infrastructure and applications to Azure. +This solution uses the [Azure CLI](https://learn.microsoft.com/en-us/cli/azure/what-is-azure-cli?view=azure-cli-latest) and the [Azure Developer CLI](https://learn.microsoft.com/en-us/azure/developer/azure-developer-cli/overview) to quickly provision and deploy infrastructure and applications to Azure. To get started, authenticate with an Azure Subscription ([details](https://learn.microsoft.com/en-us/azure/developer/azure-developer-cli/reference#azd-auth-login)): @@ -17,6 +17,12 @@ To get started, authenticate with an Azure Subscription ([details](https://learn azd auth login ``` +Also authenticate with the Azure CLI ([details](https://learn.microsoft.com/en-us/cli/azure/authenticate-azure-cli?view=azure-cli-latest)): + +```powershell +az login +``` + Establish a new environment. Provide a name that represents the application domain: ```powershell diff --git a/docs/quota_check.md b/docs/quota_check.md index d22e870..e265b12 100644 --- a/docs/quota_check.md +++ b/docs/quota_check.md @@ -5,7 +5,7 @@ Before deploying the accelerator, **ensure sufficient quota availability** for t ## Login if you have not done so already ``` -azd auth login +az login ``` ## 📌 Default Models & Capacities: @@ -88,12 +88,4 @@ The final table lists regions with available quota. You can select any of these ```sh ./quota_check.sh ``` - - Refer to [Input Formats](#input-formats) for detailed commands. - -5. If you see the error `_bash: az: command not found_`, install Azure CLI: - - ```sh - curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash - az login - ``` -6. Rerun the script after installing Azure CLI. \ No newline at end of file + - Refer to [Input Formats](#input-formats) for detailed commands. \ No newline at end of file From a79a08237fc005990d056e6ca8cbd6765e12a130 Mon Sep 17 00:00:00 2001 From: Seth Date: Wed, 21 May 2025 18:26:57 +0000 Subject: [PATCH 15/44] Connections - added bash support and naming consistency --- azure.yaml | 7 +- docs/Verify_Services_On_Network.md | 4 +- ...ntVariables.ps1 => set_conns_env_vars.ps1} | 0 scripts/set_conns_env_vars.sh | 171 ++++++++++++++++++ ...ions.ps1 => test_azure_resource_conns.ps1} | 0 5 files changed, 179 insertions(+), 3 deletions(-) rename scripts/{SetConnectionsEnvironmentVariables.ps1 => set_conns_env_vars.ps1} (100%) create mode 100644 scripts/set_conns_env_vars.sh rename scripts/{TestConnections.ps1 => test_azure_resource_conns.ps1} (100%) diff --git a/azure.yaml b/azure.yaml index 4c4e3e9..7ff4710 100644 --- a/azure.yaml +++ b/azure.yaml @@ -7,7 +7,12 @@ hooks: preup: windows: shell: pwsh - run: ./scripts/SetConnectionsEnvironmentVariables.ps1 + run: ./scripts/set_conns_env_vars.ps1 + interactive: true + continueOnError: false + posix: + shell: sh + run: ./scripts/set_conns_env_vars.sh interactive: true continueOnError: false preprovision: diff --git a/docs/Verify_Services_On_Network.md b/docs/Verify_Services_On_Network.md index f9c2a34..b6d2be8 100644 --- a/docs/Verify_Services_On_Network.md +++ b/docs/Verify_Services_On_Network.md @@ -6,7 +6,7 @@ This guide will walk you through using a secure jump-box virtual machine to inst ### 1. Copy Testing Script to Virtual Machine -Copy [TestConnections.ps1](./scripts/TestConnections.ps1) to the Virtual Machine. +Copy [test_azure_resource_conns.ps1](./scripts/test_azure_resource_conns.ps1) to the Virtual Machine. ### 2. Install Azure CLI @@ -41,7 +41,7 @@ $containerRegistry = "your-container-registry-name" ### 5. Execute Testing PowerShell Script ```powershell -.\TestConnections.ps1 ` +.\test_azure_resource_conns.ps1 ` -SubscriptionId $subscriptionId ` -ResourceGroup $resourceGroup ` -KeyVault $keyvault ` diff --git a/scripts/SetConnectionsEnvironmentVariables.ps1 b/scripts/set_conns_env_vars.ps1 similarity index 100% rename from scripts/SetConnectionsEnvironmentVariables.ps1 rename to scripts/set_conns_env_vars.ps1 diff --git a/scripts/set_conns_env_vars.sh b/scripts/set_conns_env_vars.sh new file mode 100644 index 0000000..c978ecb --- /dev/null +++ b/scripts/set_conns_env_vars.sh @@ -0,0 +1,171 @@ +#!/bin/bash + +# Usage: ./set_conns_env_vars.sh [--tenant TENANT] [--subscription SUBSCRIPTION] [--resource-group RESOURCE_GROUP] [--workspace WORKSPACE] [--include-verbose] + +while [[ $# -gt 0 ]]; do + case "$1" in + --tenant) + TENANT="$2" + shift 2 + ;; + --subscription) + SUBSCRIPTION="$2" + shift 2 + ;; + --resource-group) + RESOURCE_GROUP="$2" + shift 2 + ;; + --workspace) + WORKSPACE="$2" + shift 2 + ;; + --include-verbose) + INCLUDE_VERBOSE=true + shift + ;; + *) + echo "Unknown option: $1" + exit 1 + ;; + esac +done + +TENANT="${TENANT:-$AZURE_ORIGINAL_TENANT_ID}" +SUBSCRIPTION="${SUBSCRIPTION:-$AZURE_ORIGINAL_SUBSCRIPTION_ID}" +RESOURCE_GROUP="${RESOURCE_GROUP:-$AZURE_ORIGINAL_RESOURCE_GROUP}" +WORKSPACE="${WORKSPACE:-$AZURE_ORIGINAL_WORKSPACE_NAME}" + +if [[ -z "$TENANT" || -z "$SUBSCRIPTION" || -z "$RESOURCE_GROUP" || -z "$WORKSPACE" ]]; then + read -p "Start with existing Project connections? [NOTE: This action cannot be undone after executing. To revert, create a new AZD environment and run the process again.] (yes/no) " response + if [[ "$response" == "yes" ]]; then + [[ -z "$TENANT" ]] && read -p "Enter Tenant ID: " TENANT + [[ -z "$SUBSCRIPTION" ]] && read -p "Enter Subscription ID: " SUBSCRIPTION + [[ -z "$RESOURCE_GROUP" ]] && read -p "Enter Resource Group: " RESOURCE_GROUP + [[ -z "$WORKSPACE" ]] && read -p "Enter Workspace / Project Name: " WORKSPACE + else + echo "Not starting with existing Project. Exiting script." + exit 0 + fi +else + echo "All parameters provided. Starting with existing Project ${WORKSPACE}." +fi + +if [[ -z "$TENANT" || -z "$SUBSCRIPTION" || -z "$RESOURCE_GROUP" || -z "$WORKSPACE" ]]; then + echo "Unable to start with existing Project: One or more required parameters are missing." + exit 1 +fi + +az account set --subscription "$SUBSCRIPTION" + +TOKEN=$(az account get-access-token --resource https://management.azure.com --query accessToken -o tsv) +if [[ -z "$TOKEN" ]]; then + echo "Failed to get Azure access token." + exit 1 +fi + +CONNECTIONS_URL="https://management.azure.com/subscriptions/$SUBSCRIPTION/resourceGroups/$RESOURCE_GROUP/providers/Microsoft.MachineLearningServices/workspaces/$WORKSPACE/connections?api-version=2024-10-01" +CONNECTIONS_RESPONSE=$(curl -s -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" "$CONNECTIONS_URL") +CONNECTIONS=$(echo "$CONNECTIONS_RESPONSE" | jq '.value') + +echo "Connections in workspace ${WORKSPACE}" +echo "----------------------------------" +CONNECTION_COUNT=$(echo "$CONNECTIONS" | jq 'length') +echo "Connection count: $CONNECTION_COUNT" +if [[ "$CONNECTION_COUNT" -eq 0 ]]; then + echo "No connections found in the workspace." + exit 0 +fi + +if [[ "$INCLUDE_VERBOSE" == true ]]; then + echo "Connections response:" + echo "$CONNECTIONS" +fi +echo "----------------------------------" + +COGSVC_URL="https://management.azure.com/subscriptions/$SUBSCRIPTION/resourceGroups/$RESOURCE_GROUP/providers/Microsoft.CognitiveServices/accounts/?api-version=2023-05-01" +COGSVC_RESPONSE=$(curl -s -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" "$COGSVC_URL") +COGSVC_ACCOUNTS=$(echo "$COGSVC_RESPONSE" | jq '.value') + +echo "Cognitive Service Accounts in resource group ${RESOURCE_GROUP}" +echo "----------------------------------" +COGSVC_COUNT=$(echo "$COGSVC_ACCOUNTS" | jq 'length') +echo "Cognitive Service Account count: $COGSVC_COUNT" +if [[ "$COGSVC_COUNT" -eq 0 ]]; then + echo "No Cognitive Service Accounts found in the resource group." + exit 0 +fi + +if [[ "$INCLUDE_VERBOSE" == true ]]; then + echo "Cognitive Service Accounts response:" + echo "$COGSVC_ACCOUNTS" +fi + +for i in $(seq 0 $(($COGSVC_COUNT - 1))); do + ACCOUNT_NAME=$(echo "$COGSVC_ACCOUNTS" | jq -r ".[$i].name") + NORMALIZED_ACCOUNT_NAME=$(echo "$ACCOUNT_NAME" | tr -d '-_') + echo "Normalized Cognitive Service Account Name: $NORMALIZED_ACCOUNT_NAME" +done +echo "----------------------------------" + +echo "Connections details:" +echo "----------------------------------" +for i in $(seq 0 $(($CONNECTION_COUNT - 1))); do + NAME=$(echo "$CONNECTIONS" | jq -r ".[$i].name") + AUTHTYPE=$(echo "$CONNECTIONS" | jq -r ".[$i].properties.authType") + CATEGORY=$(echo "$CONNECTIONS" | jq -r ".[$i].properties.category") + TARGET=$(echo "$CONNECTIONS" | jq -r ".[$i].properties.target") + + echo "Name: $NAME" + echo "AuthType: $AUTHTYPE" + echo "Category: $CATEGORY" + echo "Target: $TARGET" + + if [[ "$CATEGORY" == "CognitiveSearch" ]]; then + azd env set 'AZURE_AI_SEARCH_ENABLED' 'true' + echo "Environment variable AZURE_AI_SEARCH_ENABLED set to true" + fi + + if [[ "$CATEGORY" == "CognitiveService" ]]; then + for j in $(seq 0 $(($COGSVC_COUNT - 1))); do + ACCOUNT_NAME=$(echo "$COGSVC_ACCOUNTS" | jq -r ".[$j].name") + NORMALIZED_ACCOUNT_NAME=$(echo "$ACCOUNT_NAME" | tr -d '-_') + if [[ "$NORMALIZED_ACCOUNT_NAME" == "$NAME" ]]; then + RESOURCE_NAME="$ACCOUNT_NAME" + KIND=$(echo "$COGSVC_ACCOUNTS" | jq -r ".[$j].kind") + echo "Matched Cognitive Service Account - Connection: '$NAME' Resource: $RESOURCE_NAME" + case "$KIND" in + ContentSafety) + azd env set 'AZURE_AI_CONTENT_SAFETY_ENABLED' 'true' + echo "Environment variable AZURE_AI_CONTENT_SAFETY_ENABLED set to true" + ;; + SpeechServices) + azd env set 'AZURE_AI_SPEECH_ENABLED' 'true' + echo "Environment variable AZURE_AI_SPEECH_ENABLED set to true" + ;; + FormRecognizer) + azd env set 'AZURE_AI_DOC_INTELLIGENCE_ENABLED' 'true' + echo "Environment variable AZURE_AI_DOC_INTELLIGENCE_ENABLED set to true" + ;; + ComputerVision) + azd env set 'AZURE_AI_VISION_ENABLED' 'true' + echo "Environment variable AZURE_AI_VISION_ENABLED set to true" + ;; + TextAnalytics) + azd env set 'AZURE_AI_LANGUAGE_ENABLED' 'true' + echo "Environment variable AZURE_AI_LANGUAGE_ENABLED set to true" + ;; + TextTranslation) + azd env set 'AZURE_AI_TRANSLATOR_ENABLED' 'true' + echo "Environment variable AZURE_AI_TRANSLATOR_ENABLED set to true" + ;; + *) + echo "Unknown resource kind: $KIND" + ;; + esac + fi + done + fi + echo "-------------------------" +done +echo "----------------------------------" \ No newline at end of file diff --git a/scripts/TestConnections.ps1 b/scripts/test_azure_resource_conns.ps1 similarity index 100% rename from scripts/TestConnections.ps1 rename to scripts/test_azure_resource_conns.ps1 From efe8821ffd7af26467886d10e6e692abee3ff57e Mon Sep 17 00:00:00 2001 From: Seth Date: Fri, 23 May 2025 10:28:25 -0400 Subject: [PATCH 16/44] .venv python gitignore --- .gitignore | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index e136fcf..48a5992 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ .vscode -.vs \ No newline at end of file +.vs +.venv +__pycache__ \ No newline at end of file From 00f201aeb137941f377f7124c4eeae3c09d8addd Mon Sep 17 00:00:00 2001 From: Seth Date: Fri, 23 May 2025 15:59:59 +0000 Subject: [PATCH 17/44] AZD - preup permission fix --- azure.yaml | 54 +++++++++++++++++++++++++++--------------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/azure.yaml b/azure.yaml index 7ff4710..c062782 100644 --- a/azure.yaml +++ b/azure.yaml @@ -1,28 +1,28 @@ -name: deploy-your-ai-application-in-production -infra: - provider: "bicep" -metadata: - template: deploy-your-ai-application-in-production@1.0 -hooks: - preup: - windows: - shell: pwsh - run: ./scripts/set_conns_env_vars.ps1 - interactive: true - continueOnError: false - posix: - shell: sh - run: ./scripts/set_conns_env_vars.sh - interactive: true - continueOnError: false - preprovision: - posix: - shell: sh - run: chmod u+r+x ./scripts/validate_model_deployment_quotas.sh; chmod u+r+x ./scripts/validate_model_quota.sh; ./scripts/validate_model_deployment_quotas.sh --subscription $AZURE_SUBSCRIPTION_ID --location $AZURE_LOCATION --models-parameter "aiModelDeployments" - interactive: false - continueOnError: false - windows: - shell: pwsh - run: ./scripts/validate_model_deployment_quotas.ps1 -Subscription $env:AZURE_SUBSCRIPTION_ID -Location $env:AZURE_LOCATION -ModelsParameter "aiModelDeployments" - interactive: false +name: deploy-your-ai-application-in-production +infra: + provider: "bicep" +metadata: + template: deploy-your-ai-application-in-production@1.0 +hooks: + preup: + windows: + shell: pwsh + run: ./scripts/set_conns_env_vars.ps1 + interactive: true + continueOnError: false + posix: + shell: sh + run: chmod u+r+x ./scripts/set_conns_env_vars.sh; ./scripts/set_conns_env_vars.sh + interactive: true + continueOnError: false + preprovision: + posix: + shell: sh + run: chmod u+r+x ./scripts/validate_model_deployment_quotas.sh; chmod u+r+x ./scripts/validate_model_quota.sh; ./scripts/validate_model_deployment_quotas.sh --subscription $AZURE_SUBSCRIPTION_ID --location $AZURE_LOCATION --models-parameter "aiModelDeployments" + interactive: false + continueOnError: false + windows: + shell: pwsh + run: ./scripts/validate_model_deployment_quotas.ps1 -Subscription $env:AZURE_SUBSCRIPTION_ID -Location $env:AZURE_LOCATION -ModelsParameter "aiModelDeployments" + interactive: false continueOnError: false \ No newline at end of file From 1d48919ba1eed06bb496a4695eddf5ab073a8016 Mon Sep 17 00:00:00 2001 From: Mike Swantek Date: Wed, 28 May 2025 09:39:12 -0400 Subject: [PATCH 18/44] update codespace document with model quota check steps. --- docs/github_code_spaces_steps.md | 21 +++++++++++------- img/provisioning/az_login.png | Bin 0 -> 16596 bytes img/provisioning/azd_provision_terminal.png | Bin 70643 -> 54430 bytes .../azdprovision_select_location.png | Bin 63752 -> 60954 bytes img/provisioning/checkNetworkIsolation1.png | Bin 54301 -> 63241 bytes img/provisioning/checkNetworkIsolation11.png | Bin 62039 -> 62289 bytes img/provisioning/checkNetworkIsolation3.png | Bin 82808 -> 82068 bytes img/provisioning/enterpassword.png | Bin 16248 -> 15049 bytes img/provisioning/preprovision_fail.png | Bin 0 -> 56434 bytes img/provisioning/preprovision_success.png | Bin 0 -> 22331 bytes 10 files changed, 13 insertions(+), 8 deletions(-) create mode 100644 img/provisioning/az_login.png create mode 100644 img/provisioning/preprovision_fail.png create mode 100644 img/provisioning/preprovision_success.png diff --git a/docs/github_code_spaces_steps.md b/docs/github_code_spaces_steps.md index 0d2a177..a835757 100644 --- a/docs/github_code_spaces_steps.md +++ b/docs/github_code_spaces_steps.md @@ -37,6 +37,7 @@ You can run this solution using GitHub Codespaces. The button will open a web-ba ![Image showing the pop up window in the web browser for azd auth](../img/provisioning/azdauthpopup.png) 7. Repeat the same process in #6 using the “az login” command. The [Azure CLI](https://learn.microsoft.com/en-us/cli/azure/what-is-azure-cli?view=azure-cli-latest) is used to validate available AI model quota. + ![image showing theaz login in the vs code terminal](../img/provisioning/az_login.png) 8. Return to the codespaces window now. In the terminal window, begin by initializing the environment by typing the command “azd init” @@ -46,19 +47,23 @@ You can run this solution using GitHub Codespaces. The button will open a web-ba ![aImage showing entering a new environment name](../img/provisioning/enter_evn_name.png) -10. Now start the deployment of the infrastructure by typing the command “azd provision” +10. Now start the deployment of the infrastructure by typing the command “azd up” - ![image showing the terminal in vs code](../img/provisioning/azd_provision_terminal.png) + ![image showing the terminal in vs code](../img/provisioning/azd_provision_terminal.png) - This step will allow you to choose from the subscriptions you have available, based on the account you logged in with in the login step. Next it will prompt you for the region to deploy the resources into. + This step will allow you to choose from the subscriptions you have available, based on the account you logged in with in the login step. Next it will prompt you for the region to deploy the resources into as well as any additional Azure resources to be provisioned and configured. - ![image showing region selection](../img/provisioning/azdprovision_select_location.png) + **Be sure to remember the vm password. This will be used in a later step. You are still required to log into Azure once you connect through the virtual machine. -11. Next you will be prompted for values to enable additional features outside of the AI Foundry required features. They are false by default. - ![image of prompts](../img/provisioning/prompts.png) - **Be sure to remember the vm password and vm username. This will be used in a later step. Because we are using FDPO subscriptions, we do not have access to Entra to create the SSO to the jump box at this time. You are still required to log into Azure once you connect to the virtual machine. -12. After completeing the required paramters that you were prompted for, the provisioning of resources will run and deploy the Network Isolated AI hub, project and dependent resources in about 20 minutes. +11. The automated model quota check will run, and will check if the location selected will have the necessary quota for the AI Models that are listed in the parameters file prior to deploying any resources. If the location selected has sufficient quota for the models you plan to deploy, the provisioning will begin without notification. + + ![image showing model quota pre-provision pass](../img/provisioning/preprovision_success.png) + + If the location selected does not have the available quota for the models selected in your parameters, there will be a message back to the user, prior to any provisioning of resources. This will allow the developer to change the location of the provisiong and try again. Note that in our example, Italy North had capacity for gpt-4o but not for text-embedding-ada-002. This terminated the entire provisioning, because both models could not be deployed due to a quota issue. + +12. After completeing the required paramters that you were prompted for, and a successful model quota validation, the provisioning of resources will run and deploy the Network Isolated AI Foundry development portal and dependent resources in about 20 minutes. + # Post Deployment Steps: These steps will help to check that the isolated environment was set up correctly. diff --git a/img/provisioning/az_login.png b/img/provisioning/az_login.png new file mode 100644 index 0000000000000000000000000000000000000000..89d8e56bd135d909c7c7dd4f954360c0ba80cd2f GIT binary patch literal 16596 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 Oq$sNV z_n%(c0RVn0-oJb6xu4Z08U9gd%vO>JLtnc0(C%BxN4MGzr$K(sstZ5vOGi9eJ-~Fu&HHdeBH~kLac0q1c8hZ6^XmIb^Y{XW(6E)E;ikNx4^>1 zXd8W3c!cGad9_5Dh~e8Lr-Ex&KR9>4qByC}iW$Ajudnqfcyd!Yy$Gk9ySaVr1vlpM z*_e$P@9TSH-=C%|e(~S!+mCGB>05XI+iHNm$LBfz-DGZU?p^qAl-Zl{wLAZfsXSda z5)t|D_L9jim;Jx-XwG(hvHu8wFbPL81KvB7!fpgP%?ZKa8pGv+^@*%NH5-WzlWtTFqg8s>?icz^Bn{-hcbk z;O!3V?xZzlU=DO)`v-&ken-Dr)5N4sdmDhlR@I;GbsWRkPS190fGI!Smab)ET>T(< z80q!vxuG846CS8;p_!gU?w^hs+C;c3M^=V^JoMYD8r!LXpVSbikYw}5gGrZK@3jwp zX;W*G9Mttg`GX%C>(=?%&8);$-gnt@y68{{w}FagBgSvHG6=4!(Khf&x9sGd zSY(5Frp6gosWQnxq~F^{wcgF&3aBt-nYQ;&0()vhRv}BT_k5-iO*sO@XHnlo8WSfoW;cHI3}BJELV~JG>9gj;X7sRkGAVjSi!9PkSg)(KwSY zjZm5969pCdn7Nk?&&r~2Ys7g6@bf&E&(O~<9nuETuE(XNsWnAS%@{G#JY#Lp(T(k< zL#ydPT+`pru$?e58(-fST20QRRp(gpff$Cv1=R^^=ck;-;_3&@Kg^1tR+>o+Ht7=! z`#&@&p4nb6NmAQnIr#ed=AGBz8Q{LyBTNMRScxKuFffr5t_AGDSQ@rD65jKfraD4} zL;w{H#@N14tOy7o_uaB#%uc%)W7@AzU}lsBn8azDV~ZhtJqsR|3@Rl$yvFz{+@y$s z+?HyOIy0cjCGH0Nx=F+j;}pXi!wn1x(To?%pWuC-vOh={lW#qqsWO36`tQx)v&*0C z+Z=^OjfIB7_*(fZ7Bl= zH|26do^#X}+h4ktrAxb1s$u0Lx#*WR0Re*0X)i_~dCdTugdkl49(5aFMi3OG?OH6` zBLjnM^vlR(x?Dv2YMR30TlODRUy1jDC->W}MNza>5D7nm@Ppj+=^(MEZp7E=w)(|& z3Q(i9qdR>vr5XoqNtP>Tg(UjcvJrPkyEuK?ILSWfr?nLRD7VQ1WaF~tzso~^B{t5W2@?W zMQhz0S!EVBLT(-DOh3v?P4er~k`=i;EG%Gey}>pj`mT(&IwROJy@ieAjy;h+HM%~P zIUUL}x_&?R9z0BX;v}Qu_Ul(aKtBVT9CrzbB;|GO!W>F2#fnS#Zu|2Gs?e+9!@Y`9 zQTtQyvZJBvF(q|#Xs3)3vr{!pQS1KV4M%*5n+yX%E)TSXqm5JY1D=RH!>1qrc!el0 zuO5IOXaOu*R5of&4`BH2*2;s3Zo~uQ*2>AnBj`?REZlsz2jTaK>oBm=((gC=TVcc) zg>w)C3j&c97DCiJP<6rhkS6#dZhA#I_f*uFK=%Dv(9XS#*+zj14?v6c6sp7X?FPx; zawT)%>hSJpms7?p>E5HJrST)Egj$ooBFuZf=!tDBbUj(IkNQ?B3$^J&-WAmXu6Wr~ z6WD}3h533;wPdHBgktAhoX4K-J3o>L?F+$U#LH|m{lY(dekhTtQj-z9Qh$)1PMG;j zAbNJpmV8^9w*6ftJiN>%lN|PEr$#Nds z*3CicK-w;C;qi`@SVJx80lIL%v*z$Mj>G3UC9(7f`M_H+t!SL~Q&cZPBL*Ed*EXJP z)j4_?XLxf&7Zib3mQ&Sj{f%^p<*k>4$qkIoozH%i20Xr0+9rBSG2BpB5^Wj_^WjDu zxe>-KBjf|T1%nfUri9(-r%xuH`DR$lcS?!`t8YK(9XDteU-Q$5mPC>mGANtZ6pM86 zabj~ZgNnD?CCy3rNDp^%*09@F@94!e6GCsh_=`hn8%%f zevd^oV&fQ#C$l{c!1!o&yQO!z%36UiWi~T^HEPaA=;Kekr==_Q$x?I{En^0G*szQ6 z>sI?atIK~vQ;=tL?p>%2Q^}tb!M)7?^}a(&#H-@kb1TsCE=N#8L{Wv zTRne0Ut$Zr^ zHx|0sH%zD@#BcMsCsQPZ=Th_BuTCz>QhYmZL84XQb@>KGFc;|ufVxp7P~17ziV298 z&eoDsfLXyK2*kUa$(1xRcZ;1eiDtse^!cn;`mNkrNaLkrq(<83Ft7*XfYQCBVm`M|Z}$zQG&NGxsZfrfSkr9rmyPhM7l4uM^{Wz@q3xbVV+5 zO>HH;pAF#wvRmGssZfin;4h-)1fEM)-(5|VOW6<#R*xqdQ_otH+$JKwyx<$iY@6~x zZZ(njz6Eca+P>}RyRQ}4s-0#Z3<}9Rlz$h{`x9Xt06)#T%^_<)WvnvSP7fHMPr$+W z$?{@_UL4>wkAk^h)!^B7fx`BzIi4Kt3^n$TVzf**d z=rv*ohn-WLraG3b8niCCtr^~y8;DL~N(ix-$0od~o@E%7eXbc<2z=tEbU4RKB2WFw@pDA^JX#EW@FU#JS$^kR z4}SA(dHf+aed_5~Ntl>|>*v~TqSx_xF5|}cCy-FXe6`s}1~-yUO*^RPe&KBbuk!g+<5m z{;(dnIHe;QrT!pCy?mJq@v8}>9^)+Bp<2o#l%A>b_a7M5cO7MGn&8Y|)i_$Xuxg6n zX?zsq*X7eQfs4hQ-|pa}8dYfaE^bQ)EY8I)%pM}gGIDr9#{$`RUQX%6!i<1!k1K7* z5aS+=m!=>Ism^+fc*((f(AmeqhawpqIw6HPRh^p|sj01F1L>Juw0nO2h247NOz2`i zOv)Ne!bwH`$!q`X0+(*Rm{M@*6y6~N!o2u=aEw$XGoxKHT z@x%z=tVW*gUW?J8xwnagc!=U$FylA{aIk09GFJ6ajou!)+V|VYE4gO2O072dvL{CQ zuwM31E7rJgjV-sP=Ed!>&}Kr~>hRXJF>w zc*Xw5h$=8)V{=XVm)gcx_=f%R?T(UkWi79 zgJ*o!D(B9c06aum5-20q^t44jocDnE=Z!Y&T02u>85W^cr+-Dx%gnc_Y z<#G{mUjLd6|3PIjL-M#ML?fL>e(Lom*60BRFN#{_sJn7l8U&ywD!^U`o2JvSlP%(s zHN~;etm=W52w+EGjBzBJ*qK0QBRI!0x`N$_JkRTS;5Hva&1XOCFu)4I_q8)D?V;PW z2vP3nF_Dt|d4f4SH&P9l&TQku?NXhJmb<<$x1yKpwc(D|(7h5JuY+euXNoWHTwsI$ z*}wd%c0;k~+4STS5R?u|`PSG5ULUeO8ml~<4c)JeED7GYF1pzymJkfPY?WDmc5l|! z_X=Q=E|4|41o2X&F1sAIQIg`~C7i7Pb)EVtK|tcMgYbwpElb3O+bp6E%+s}VoM#B~ zqU^QBu_?TRH@Gr2!rhiX=KErj^udW|pRyyX1ZcEJ**8RX!>r<0SC zopeAdo>8#zZaFewCJev$hcml`flrkDt6Z@Bi)#nxe^T6S^GT+MrOMwPP6D+ji{T=+ z=-*+k_5GIARa|QSak^ia&r5V4gb9Hc1Zjh-Rj8@)feIy5*fMrnJYat)w$5LLT7p|5nmF-HeMz+ zj%3Ql&Bc@ry0@=F+2{}f0{hm-;R;P_pyk+iAF#PQTe`C?N!FqrVA#o23q?&Q^UgSE z|24#R318B#5UpTe+24RAW~kS%OeO5u9S8u< z@#A$-II-9;$8X(Dnr`dVdkdab%5nY`B<0Z@SHmamF2Xuw(enJP zM1v-?mYi0k>5B3QW$5!1$FaO& zCcm5x5c|1)HF&@Bxh~C2YHMbAj&)ln7g84Fi%4XIiqsKSL=e|;PdZmDWt~hgxKW?a zP=ooAABT9vyXq@OGU74^_1~k1ItB!z>OVug8^6CFG{{y`KOKCDNhVq6AtIKiSaIMv z2ba)+Ew?>CRQCL(XJhY$q`#iA*9cO0)>*0>6h(Rq_oJ2hH6eN$!J7jaHXi<0FD{40 zk1Le%N3q{leSFuE*Oby1(aYG7G5h1e-zwm4OoteW0`C|TCHi!aRhw&xVxlHJ9WY1? zmB+YBG3piF+UqM%z=L+*mxfb>hdpMW-?(`3TO?5*cMh8fS3DC0X6#Jgm=x|v0*-vY zdTKdMaFgX_o6HL*IW6jy0dGQjMC41u6ZWt}dKC2xT-;PO{-;pR9_W`xV<0?G1PE9k z8PRh!k=(vQJ8HW02YoRb-qrMb!SfExMdYyhqL~fMGus4TYy~&f!D%QRh^$klhWBN(-zg_wX=qE$9Qfz(dkZ|y zxe1PA+f^p)H#KcRLwP@*{PtNLRr$beKz#qe?(W#v4GEL9%jLfby5A%Mm9?g*0H=O7 z%x2V>MRvrfhuBNe^ch6Q!}M4(?QXD%QY|6W?LR|VpjbYqK6?X~7Wb31uyi?_^lZhb ze=fmlo82vqU{4C?0KjApp6vIXwevS3BS+v_$S`d_>Mwo16feEk&o#`ayUEb@f#Y^D z$KCj~J7xpKCabaciU})~;?G|6<>~dybgi-x(wyouMp7FIj0$8KuPA4)h-(Rh^G33; z#WDYtuV$VrD7B^`X%8dHZ=XxcGxejAGVk8hffyev4ll&k%#awI!(PwYY9z!QF(Vk0 zj6P#vVgrWiQ~G{lF7e~)K7~HtEdqzv&5utPo~N2jN=-}Z6bHj|Be+COQ1H~$5vXlF0K2t(%{(<0Z+ zqP=e(jQUkibULHK7ZXlj=RXK;Ih5=b12~}KYGbuDj12YIvp;mDOJqmuMdkYK$nmKo zHyr%+oSp(+T@|Yd49;IV7s4H0?sbnMUF36VFp!}wKAy$Y3;_7#?3$8t6KHrRuaS<+ zN#t|E!tTo08FE+aTWie?adz9t?AL7W2f%7@k8oLqGjn-uNQtJ#i<|-L(<+C8nZKIE zDl|Q4@>wUnaVQ~KBQ1X4Je?*wfcxTeQj^Ag=dYKi$Ho7!9Gs8{ae|hdQ2`AJYyTVA zmA~&!uGyQ!g`HHS1oyzwaf1p^Zwvb;U2okN*4Ex4-uqVk)q~qgJbe2a-#D_jQ)uNOWeCFhmuJE(>e=}xC0)+m@%_&c zi;-y4XF&Ju!q!zn_v!SW%X!(Ojgt7c98tlU(lUVeT{bP7VUHZn*!bwi7c!?J_5I)G zxq>h34i!%M@nY-Qg0*ZKQq`lt28Ers5>AL4t!0)|#;UrTv2NCDX_Nj$bI-W#)bi04 ze^IqsDxCwZD(=fO1G5Qxh-hC@D>n`sZK$5`C#*g*?b|+6y){(2E?_U1QfLoeA?)=1 z>kXivo~OkK9G8hH4w-g2js?nv>8eXJxiv|{{2l#)J;p^u*SJe=qI*az9{)E*RKS52xm#F zNnz8(EduG?@&d*Bh<_BJHYe;)P*&49q!sN#h`P7hwEHlFWJnljj4<|+9G#=zqlq?E zS%X=5V`dsKnhM`p%~jyFQx}nb62JMUbiGAn`DKKua?m)y42+fWgw6Hi2x@`aD?5AZSOFgo%mT zjx_7Rl|I+!ALK?4^Eu}ZCN?oQJb8ssIPB0?mZAwg^BUS)4_r+c0Y6<%-o&+(rbs-` ze-|R5%8j8HxCD6KGb$+mLEaJnPA0i@jIQ5Vr)u}=fl=(W`J>%~^ zojl(`G8YH4S$%)b`0`$5-;!Mn8f^0$DpN^NXzc_Z_>~+BmD4n{2aN#~A7RR9=D2;) zmJe-2c4z@~+A>5%-A?w6<|>WJxXbPcU3`@1$9v~m;Bnvl;`!*%aGOgCPan}$eNb3B z(-AT@F*m<>?E5q0OnU`?uVhG1sesdL8GpG(bLkT2fd-)S%|MmltL0MzXYNrxe&A2- zlCWKt%M{hQY{b&&SDqZdyMjJ*hiPU&qcD$C-*3H z^s4{Jrxj>^J1s7L)yOgBXz&=sbn{nwzf1GMQRwm;T(QR_=jgJ4wkJ=Ek=98pHY3;I z)Zda&c+H~fPFrDgU2chk?~QsyCyruU>5W_ous+i_*i1 z|CfP(GyT8DUB>La|BX8B_5J_fpnG}W=QwC(U$x!xzoB=<9>g?^tn4L4!W}K9Hx{m1 z9b@M@482s}Jso?kmpjr)OT`fl-~RE?G6SFJacwf|>AI zt&fiix~)}tIz>cdK;~P#%zyr5x+B{6I2I?y0BjJeYv_=8bJ!d@r+(xWv=67S?rHD3 zH8wg`_Q$6e`5is<`Hg(8ORhnM;9Or3j^BN_ZAi=R8hcS%PXuEdvEYTv3g0${P!*U^ zz37aGWR zaKHM&XS?_8tb}pjwfjXQxh%9Sc$;VFHH{9eR52)Ofp&?~X0qdD3(-y1%9)Qc^}By& zdb53SmAaI8pKfHN^Im=>ph%)5!*R2x0c++X`oHt_F!VhZCNEr=Zotn)H<&jaIcl98 z@O{gHx$=*o%&4`3&D9auZcYzHMl9_II_DmU1IRL~d_u zaX;f>>DBo7ziB(rrkz1%D#deAvV$m_<6`r(gH&BF)on}q449^aWbe$OokYm=l!4tI zxriU)eRau~Chm#Bv(>+WH>y2Ua}ngJxI>I9nHBJ##%%4^-n^Yyq!xIb-ai+ryAwfk zeYe$e-SEIYx~?p#FV7#^cAgIL(aDl;#Nu|1*#5@-BZ1-@5Oflx) zhTpde#4l*qM3=uuZaz6zwToZI4QO48`+Be6{=bzuQ+-x5I35#-i#dsXmpNi>l^|)5 z&NvsygvC)9H>rJ=nABQ1cb}HGJKLs8j7&580+ll|gl*G|&I>`m)MYm3FW?IY{^X)J zZ|J#(R7yF{oVk>fYpBcP_b7<|3^~2nILX8;ukoxcp`}Gl>qTu{av!*urVIF>qZna_Zd_?}B zpSb?583_1fo1NrXdRk9+f!NE&AmDHBeZwCjUG3(U|L%)s5o7(nvgT#G$d1Np(Qe?x zDA&BjMlE4_)Me;(rfWs3d$hy9~l0pE^J_LSZWMB2}U zJAbs!xZAI=Rp3U4afyGM=fYp5bKHJ16mf3zmb&`b?SZc*&pL%OrALo7wuTRk-gmLk zniW_aIDfJ{B0DVdIBGSox67NW ztc37;dB(25w|^P^v(&gw~H&w^FWuya{WqGx6eiwK9qY2Jl{Mx)A;TINVtq>Ost}5JR%*h$vGSnc+TjH zcTe;r?(lPI5u%;*wTF)UHn(Zgh=X~rw=$#Dgzs*L_AM12B%!^ueXH$9)xp~|SEWq> z=km(-`u8S{F-h9#?kfiB`spThP$WN7dsQ+ZEwm{C60T7>+l<=*=QOt&1I z9x32p#wR0CG}7?&s)jB_np1eKLQP5Vg>Fc)N$;I>g6hVkuc)KLSPd!X!N>Ed`vobV zQ&PPZ-0b4h3{4p`ykd}n3WHoDUoPr1DP}Rog``P@3&w|tpv;d@FWY(O`+VXpPRwtJ ze5_D%Tr*>t>>hl&_~-gmptOYo=!BY8l%_qm} z^O)hMd1Do`6;Ceohl<+2T1$OL5YagJeTBy9MxDUg$j~Z@Q4-=a@bt!#-=hz}o3Ue? zONKew`(GMZ1c6Ce4r|I1Ri~a>T#o|QF6)Jgb1)g!-I%z4@!PinW}o!&@QW|G;>8<8 z>c`FMMJO`&r9bixa81Mar%!_Dqks3uWx6Zp2U^d5CQa2zKf+Zkc!B(FA~@0CT4?=V z$4_1=^?6uE3rIY)5PMe0apl1PBST39Q>f?9BbPoRB77pxwtZV|A~nMa{7J$2HYsH3 zkh9=cMgM zk50)r(Fxrx9eI;^Z|l{HKJ!z?t#@Out055rnMZQpu_0+>k35N{;!ZPo2W6OwBocjRTyrd$;S5 zli`9(K~aW`5iG~ryt-u9z5DvFlGmAQWZO597b{=#y}ojB{d0KTmq&fadUNuWTsr$b zgEot=RdlHQ_BYEHk;@9m1evmB^-+UPUP!U6M8?E1)#>$Hw^?Db{pG472>tSu(uz~2 zjcdGEdPJeM*@31SmeEcp;c=VrjmxfaIxMUx1+2yxXNhm)s5&8jM}$cMrc|5>CA(9m zBkKsFimHmKIvgopsJ?!Y4o>mWx{Ak%w+ivTudKC;MCmP(>Ajci4v~;L1~UQ7WU9L{prr~JyU1F&FIqJ*+8<0b5r8XPf1M$g6|hA($o3BrkliWxy2)|4O818 zjjG>%5^EI7l%vfzQfKVmUG5z3SBw0qo#=bqSP6=mAe(NkHHEloDs5_A&bnX^R5CYj zi4WPVWwRVhG>FvIu=lDq-k#nWrOR?H-KME#5QI&OZy%VUvuz& z)R!(IOz3pdF149euKZ!^>jY?7Dlo?X+Pr7c0Bc*Vyvf442PkuV`Gt`XXHsF`cI-b( z@-Ma}!P_S1wK(y`Gg$p23RrYLvqhUuWiN*!O?JIf{jL-3h9q+^n_*C%; z;V7~5UR-a^-eGw>5E%?Wt)>t zyc={cC9U|_newlGk1)Ky_;o`^GCu1X_Qk5I$yu7oCI0F5IpvQDa-CKP^CHRzezec3 zB{}-=YU_92+<4nR@%DP|*4ISwwm_RIt=aVKkbpk6uUY&70d}ivYqty_$R<;#yC;5_wa+T8Ey!LV;IoWwAy|F6Oc?Sa!l0s} zW4kZ~0fpA~Dck8tqO|%9Zyk7HQ@nx(rlIztwLo?Y1P$_*``GxghvEKxlXf3iYkk{l zXV0pJ3oZKw7c-762Y4Qt(HnaxdD;WRg&+zRNRGC+I>%ZCB#ZG%faj8iXK*v4s$j2Y z6FN)^(h}!y(~J(=Ahi4_fYiPY7FBic!yfPZTQqA#xTwbZf#v!aG7(mtCl$u{Sw>?o ze2=hQuX|hc)C;?2u=*@Ah<-&bC{qsJw>8-g+McXrzJ;kHJ`E6gStb*7B@v!f*2)A(>#iO!IwNitILXg{dv^o$lR- zH1F_uo^&~buC$pdi;py?aee@2DqW!U>(dhPx!^ISVwIespT!lrL;U9Y1*(UiUICu` z@-&w)ev6jvPvq~UWaOyFGm2EvDmqp@$E=p{(@&(tJVd<=+$sn|8g4z>`+B_sps@|X z*Tox`7S3FAaU|!B*roWkuSxTjY0WrZDF6`{2Lm;}Kt+zKm~ir2p5_UGs9a1kFOK+} zw64c8lBw?)Shjw1T5NcL9%|!D<<+_(x^rGp7ZPbUcTPwwKrlpmMz37CZHvcS{&0mtt4Y_yqJHwsbaBh8YLtJe6vA|k?C@t6r|hbW#ieXf-)XIoEeLU^_kh` z$(A2FERf#MU%^Ydo-)2dFk%93zKFS^7O1*%?6*-kybQEW+OnQ*=Z;qAOn3&3a&$9z z{E8yt#iqdgR0fDDx^pWmCL`s1w-Zljtu?Jh)BZh^(L=}V9|nbxg4vF_j$m6ADHh2U zg&1R$!D*2@GM1JFXJDfePY4bNH9udq@&QqXLUgID*Kc&+m^a1Q5z^e&$MsFKOSTXU%}GfQh-Oi1{FLaO25H0kQjJY^QVYUWPi9 z19UWhrr07~Rl{M~{x9%ky39BAj>$t7=o^m)0#lm286c{$dCxnQq)#A5ZJUd!x_ue5 zl`Mp^r4(AsR;gzzuBD`dJUh;s(U9wPm;-R-1!$Ets!A8_v2p6zWv6 zFl`{aPzAv)1cG62@@ix;nTN%gCiY@%u9yX?LCm6K3TN4Q1eL>Bkm)lD1~CY@uSkXQ zir3PlmaJuS=;2&L^u$9|Pe-;CoN4${O;Ni~`_9Ni2(i0EC{o5e+)*lEC{n;5Nkcc> zpjF!xsOhcZ3ha$XFb+lJ3g)El81TufdOSKj?*5@pA~34T2RgEenKlc%j1_iH3wz>w zd;$$$qFzdyuCTvNsPiSyvS1kf|BNz$Wj*xulV!^u^&=XdPnV}9Z#4+C>kHw*D+U;N zj3nRPhoJ=6xm}NlIj@Umo3zxUP}up4Y}yC(LHgai7?l{M6$^H9*?Xg&_T_)3k+b5z zrjgIe8;r)6XugNa;)&3>E9=i+w<}bkHqZXrs|>!KzZyU2-*>0Vf{s(b0eiEtg#9Li zqXPENsRzp9My~d+3|YHk9XTQgy*ddyrxXM0z- zZ*}KZ-|9z&0LW&d*65qTez1H6eFyA{qUgQ(_0_UtcHxmOK~{URsM1ghGPJI4BL(Rt z5_cqYP!9xr-fu5mkN*S^B%)Gl@cl?K9fg=ahq8vR=&J2P!CpJ#+*byze>E7}5htc^ zG=geaRB3I5(MAPB73;FowOcD*P2kI=uJ6=+y7}uA6P9Xe0JlLA;3pTFk7fiOjmlw39sZ=n>pP=^#rEr& zrWu(qMmVYe`V|VPL5)~bf*p)UhElOM47vu*p?SymNSnK?7Im*hKtW_JioupSv1u3{ zmV=DPA%fXQa8@1Wpr$u#P%wUdfgYdmM{0ZFqJlul9OQH0E7QRi~;#J(JM9cFf#Vb!6%(j|=RFm_K z#b1_$7F0zuPL1o%*j5+&X63!fknlVs=UxtZ^QuL`=KeI`E4I@sPKrAwNRGp&6^E<& z8>#1Lk-CO!dN#c@7@+pshx-}8CZ4FK8f6CIj#7B%PA!DojUq><~au zAz;AamI(zSg*K_9Wvc*B3EsB2EYZG>fDp&tA(LtrW4XtB-umW$s;F2wH;qva$! z-2$cB@}eZ=Fn?;?zrgZBp~a_ggQ%T~BmNu^pc%d`*nF5^?3wS&F?SSUN+KdkY>(~e zaRV}KwBDy3c12?abqEL5d*K``z(+t5 zlb8mKSlgQMT+1A3%jIY03DY*EUDt~mZrw>OU3f-JCPNpnVwO=$HFNP59+vG=C1eSG zq_1GE0isQluh{0utry>&FcTU$MmYuC&jS`7ze`xy-RB|B@A~w~Aw`UZ6dFyb;2>NC zs5Jp*fm+I%D#ffA+xMf!>`o5ch+@>4>{?a)wf2C|*vUA5i~1mjWI5|!RW4&*_S-rY zSxtUop*!P+lcWKtXi}ym7HaXQJA3_fc~?AR)8{nD-%p<}%iW4bYvgE1o;EGuFwgLt z)?j?E6!w|qqyFpeL!IY`;d@S(zOk46W63f*9eVuA#!x~&NG$Jtn!Hp>GcW+0M@*X1GeLU)Ikaens+>E^X6ip#8ZN2S=?|{<&&p z5GbOvQN6#wRyut8rP3l51e)7?L@P~Eqzp8AWK-2MmV3>#nx)sHfwkAeJjzk(#TRfY zzTdVii+>p~UF|BMQcOK9>NhWHv1J-=YSGqXFdf%yP2yWM2;!z?!tjTDV6UZC1N@=n z;5?PEM2!cj5zkO|89{CAvqS*s7-dR2>PF0e%8RKD=XbXjXp83283#4Irl%4QPKX4g z`a9%NgV(t%S++WCg;A zzeVF2X>*RY%mSFG3vf2(kJyRYatS;HQdJ{QSUw?6eDZIE7KIj+@xLIqUN5AK>z0wK zr^;N9&P^W?KnX_BqE0s{T&pgO!sj_90-dk^B;|?U%zx!z0KmvU|eaH1nV5uroJGe@e=N#-){ zw+1*=1@OWvb>+c#D?id&Lm&<%;YOjz{2BfWSPQ-JG?h2c&qRU~y$ZWIQ+72Vg3+!$ z(cS!aIXxe|zhl>?@nu3_B=s1709>b+H@9C79zXmy`xuSR63}U8e{F#{<$lzSssOr~#Ca=-%U3eh|r_5HI)a zqnVWyq(a05yR6-@g-BJN0PTi6_adgFyihdoTdW?uP_uu_wVA5lE^FPwxGzBJ|0+Q| zRz=25)KHm_mA;}-OJKwyjC3a>kgU`kyy}4`iy`A0yaYhVAW<9{3tIRgMpATwA1hi= z=D{r;2!NR7JD%j?c^&Na7qW~%*r6LyZT44Gx8MsH=?sjV9AdrJ5ILJbQiR!3#Jqec zxwTlV*e~+obh!08lKyZ8CbBP-%z@Ml&yoUlFdCQGZy_5QspVAnJ=iYW^hh8hB?4Y1 zhCkyakEskhS*~(44f5nqJSkzMfrv{rO zw)IP-Y@^o2k-kRGWq!m3^v2+zxD!cg*WF>RA+sF9ZiEM7rFOcFw26yb3VUP=c1~zX zdy~s&?7^pAuer$tyRE^w@K4a-(_+?5rx3Lp61DdJ>~>?t_8oC9CXf@Y&z8v80@~Eq z_tmFUDsl)NA);8JrdSX43Jr*`2uNqci*oApk)L!>I4{WT#Hu7HO{Ll8(Lu0|K1QL0 z@VnjH&uxIf$6nGHy@bx}C%eHhXS#u?;ip;YmoAPiAOEQ@-dDoOIPpE`RX{CY*TjGE z{6Nn3yWKE#n*-aFDl~K4jtm>cVPFC)bId(mQAJI>7a+L-2nWD;3ns4&(<%^^g z5FUz9u}%7MIWKZhSJP+lLH74YnODB;N^v0g z6xMc**i3LqmbG_ZdayTL8{L~MYfBTbdX1;Um!w{#SWh*xXlwTlg*YgHA%;#8Pq5H} zY?L6$HiNf~M9L_O?<;pSDRWINyNcfzs`Wk+gsB7yfU=rG6Xr%+gy8<1nhArpu-SNf zvIpCjV^HVUWHyYdf#4b8Mllh}-F^FJ?;1c{eO$n5q6;f+ZvwApR4= z&V9gwu$<(ASFCsx(M$xZafbyLqHjvT*kwsLcKVe9;vedvzZOvw8Cv4D?)=#_8x7Dm z@*JZczA;r}Z4bcxdPgOdnpPh$c636eh^wmZs3~iT62RtYcd4`MDxubq(f|)@K;k(X zElE7Wek9u+cb_r9<>ivF}}THU&uqEI7>qE1cSuy^~C zD0z6n-I`O#5yCfUt2Xa>=QE$oqAE+;#MPristDpr9G=IZQjh% z`&nSl)2XP2TQQuzPStiYZok4sF@L=IZgr~Ir_A(UWx6S67B5k@Wp<+I>Ym9ge^mw* z8FxpHg{zPGH~SU&P3K*3U5NT5?3uLGQYLizwXrIp%?u}H>_C`37mi6NZ4)&z1V2j8h^nyfGRwOUK?(z9L z&+qIrL8HG-rd|ne+8731{8m~&xiDOn+hoJlIbE2cRG(RpUP%7%#XZWuQnEw?uYnA9 zduMach0M}{yS;9NSJMnM8&tgb>GNIV|Btx046AByyLbUb1VlnwI+PS7r9nVII;9cm z7Rf~jf;3XnNOy;HN;gO&sid@kfb{vV1@5gop7%Y^d#>xO5BtOR+S|3}T64}Z$Nd}Q zcFOk4BEGC>(BWK^mp&T)DqAbHKXt6T`&(;;%1+mh$U@wU2w&`tx%V(>6n^g@v+HLw50{Ys?f@OuF599G|pL z?`?Bbe%xk63`p-n`0&gG^R&)TjA@Z>zmKNJ? zK3RFp?xo$nw1R;4BQ!y}_e$LYerWP`{^E>Y_Qd5qu+hTN^@+~CK`T#N$;lJIsYl&@ zfBfFj!0a_&7>pGnXAsXs@6%}_fLlA>!U`^Jj3x;iV$+ND)ywf=KTTD-;*R1?O-ZXs z$w;IqRr+q*xvqfRh@>AeR^XY;6BElbtAt!cp5CDNpa+xa@9kn`*VqUPYkfdYo_UcV z?abXojDN~tT;i-`zN8Ot>m=ErIyAn48SpF`Nf9 zca4rtcQ&Q9Msrwh1tT8co+C~?KfPDSpll*Gy7LRIyHaoLiRXuzp{NZC z86ZQKz8x$x!Nrk&?b7%NHCgSxI$_cd%Q+SMJK9g(k7&Ig+vLJL^1>wCbo7iup)BlU zvRvC&8s-dHvP3w5=ehD|y8OA1$`;Gp5sT%Mj97`?0&`DX zh+1x!R5#adCTC^+#=#DMGTH@2Y|C?`@U2i zE{zoQ03JOLC>?4qB1Aecx_>-;ItP!~uA#a8#yscxn9acV(s?|;YVQ|l;tPq)nGsMV zF)Mrn{e+#2HByww8gN8#8TU7K)wsFlqBF37PmYjA&Gn4eJUlp#Cx`jSg+<&OO_5rB z#vqImdt}_M>EKOyZt(p)>&WBcjr5G?m^Mv?iVd|OWKIE=Sx%w}0~BQT0cTcoiP)!p~8BB>Z9 zM5nKH6^LoL4R#Ib)t+7m0q z6iQd7s7%2Rsd1@!|9SVj2>$Oe^7vM7uQ1@61&J_o9J1lZyb4V9OjCb^hzyP+>6?e% zE3|SmlZxoW$T%sy&Aw!Ag6>Xo_nzf-Is4jgMB)0_3E@u4hpQD;Yf15TsmmLFD(1!N z3l)aXd`qA{k1Fy>yD2P9RK8;4MH#gygiw8G?g@HA(-QedJCc?4*F_oql5st!Al(s? z%4$6SD!0k&#i<5qb7OBC9%&`6cLJB+6WuMVpd@PO_rC)|9OxVt)@ZT~w4)Y+2!a#> z11{qn<@>ndb|AHf_JnAb2HS;DikIhgI9F>fI3N51w?7XEMJJ?m)U;Gd=%Ms^1W>pM zt?DLoi+QBAHfA9mH>y0AHH8?ms3!S2haY362p?qF{R~$W4PsK{+4Ww?<#_dOZ9<{gQ zRRj$Z%j^T$MJ=-CE0zY26#dBDa3OrrijSIP&`~53aD}v*>;3tZKfX=r0T0c3*ixS! z%$Z77M3Wg9E{wDKy?oR0{(y6`c<4n>vUx!!pCV&%q^O27Oo+)yF-M+*}FY&jOOAm`YH`hw$ zm!36~H--Bf-48Fx#JVAKGa3IYdpUWG1pEb??yeKPU-7xJp(?|jE)1VG!(F{IdeA+E zo>8lx=Ym;pi;@8W8M^XfxO~pSBgGY`v+zAQDluDZ#l97xjg~P(Zr4EsxU3tubS($c5?WRH2W}{uOMY=oOAkOFyVu2Xt6ti)=(ReYB(AEtzgqV1)O{+~7 zT+8x$INdRuwz=NEdCiU(D8PI2Ck1#5fS?|S`4(EMwXmOIN~>OI(4P@AnWA5crsjai zym+Nh`%?ZHH-kbxFf{?NuHPD~z+wJWB@DWx0B!QXn{rObG~iFv1T))=ORN^)*V}!g!Tzo{l<$gX;?XfksgFvTZ|)BwKDP)2#?t<+?or$KEfUtX)2g= zT?^=n`Hr^Mx7EuTBRV>Xm669G+w4V){H<%jS+u}rRp2S(dnqt~6Opr2dZCuqK=|o7)t;??Q>(Rw(qIC4{FO-9JlxIc^Wq8XGPJv=NtLk)@qt6QOy?&nX z4N=hqf$NvLq=MRNW}}s9Yi9T$F{h_RuW_}XhyT1ma&yI`&F*iy#0_b2%&YEv*kH+3 z%cjAIn>ZEgO2iXpCf*Hv#P8oFwu|nL*)@e*kKLC+gF?SA{7Q#1xq6NdQ9mppRqvD+ zs?foU5?5=B07OJPk0OECLF&`L8 z)8xQFoPH4!WFRSa$>Ms_;D+V+E#0yTBI)52@VnZCy;j-UaXx7|*+v z6W4eL39>A4^mUu=Zn?j&t6URr<#8+FnB^+me-^r=1+h|JfF51V4bic6;CP;)6|P_54oBVvFGUI}a(PS*Qi>3sDgNiBi#DtNce zj^(plPR6Jt?7_sJa;t(iVw@P|qM@pn6c^A^_$u?C&ln67t95vjK09jA)6i<}|b9Eo7XV7ov5boNx^3X7gv3ue3EodFm8ClWyqGW4LhG@OY<5I$*6 zcY80fGQKN60adeiHWt+uOD85sry%c|lpa3i2)lYY1Rd>kZ0ZOG3~4KL9@-oVQ>+OYof3vsxMs4vXMMwCj7n9=JTq ziBLQKu|}Twvx$b;x?M}tiXf%M!NDb%!f_s&0|z8hEq|B@c@$T|#q_5va+@ZTkW`Sb zcvZ4C5P8!5o}ICN4`c33afw`fhHnrV&e!NZcu;AdG(WzpZIi#Em%lvLV25fyt(Ukz zzWijaXZU#kX2h-Kbe-$ldm!@mar^Mu*#5|a-F(_HF4z$@1V{Ay!3^ytN=IE=yW?H@ z@Ps%KG{JnfahEJ2%b2(UXkKqV-^5*1KED+*WYWJv!g7T`3`qhCG;|2)@*}*yF{GGO zThUHfmV$e!Zk+du3gW#qdys0C?jYw)x-Ugov@Fj-;*0C0EOy>O5(Z2L#_-T}48i?{ zs^xSu=0N4us(5I_Q;V1Pw=2)F4I+tIA9=Ei-9&sRV`%(_DWoVsn_glNDk!QZ!cutA z@=dmLkz{puF}`vK+uA==9a>J8UOIzzn+*_@Zhuhz@~{d#h<*8NIFCxMi7E{Iy4<1z zPBC2AU0D%4kd2qNvfnt)U?wfNb?CPwoL*d3@jORTOW3kJA1aqu3P7$x|NPvUKq+jhG_H zb!RORd;Z-!MXtd8AMWF{<2wqU_WS<%)qD`G=92NV<^8GVrV-dzrVYL_qEBli6U6NE zt;Ng{G$7=cVuyltdlVjSb)x>MmD#JMZ%677>|?qh+6NiWbtll}Mm*+wt*=5jx$~is zK1>}w{|UKvE1&eeOe~=@hF7b}1-d$jy(^Fn(2;}CdY8Pe@7>@RO{R3N)4TdHIDK9< zeS`MX*T{NnXpD(zyQuj??mub_e?wW|ypDS-ttan<+p9p}(=4@_f9Nyrn7_*=#H#Z& zgHe+iO4W|TM(tfve-Nm}eV!If^U3V&IV>ZHP;R|#2)*mef=~B)S$m%Ht4CZXrcL9J z(1G2j?Uds2=f2G8b80uz*U5K1-wM-QbJ8*cnP9rhR{#1|r zX_Hh~yf;(7K9(HY4WfE(x3_e`leK>*NHd6;aT)WflmgDlE5 z-A+9^$uFAjKlGJW*n&SP+Ev>Kyso=hZ#QWeol~nZkxjWf8Opnh*Z+(m(b$`IM(-b# zRCVUa-rdx*nsA~4dF;t7N-2oTDzh^08pm}t4TZ?ehvRDq3qsn4XJd?M#T)d=d{#2e zPD@xM4uRi`>tC4x?YccGsK zq?=_(Q>gDG5L?A?vua8_55J7>`rOELxRh8xV-fu?nn1zB(eIeAf=21PIvOi(EkR{o zate^C@Z|)!Y64~%tQ)|K=7qU;{7}mkQYX%anP!EGsLd&{5_`%?JdyD=%SCPqQ+B9_ z^Z%Xr8hCj;B%Rw(&u=YDmC}*u5N&AmFzx^Yqkg7tXfFTlF<)TER%($&HpeHGDzE(1 zqNZVsySUu-=}(KpV{&)frbxIKW+^{4?A`C#1T91R5p%Gj)K$UXT^$FgtMf%_%V`&$VPCWMo*G=@S5JjLzy8WO8h`tv{5!61vEH1{K!E3 z=Wl49OQnB&(YCb*!d(zvJmZAd%wZHCZjYKa%DmUhr0m;oHjnQcA} zY=Nf}q38|-_F)a{m9=jh`Rv+akP2(X2Ka@&)j;OUUBobnfIp0~=>19rl3R&w&fwha zQk~=SG-QegeFliFNC8jQdR-+VF$Qgm&H(4|x2&fxAqN@t=Sl{5NKilag>N68YlThg z{iE7aMm0%8Mch@r8EZIo_kZC65jgyGfuevMk8Q3^Y=AR=nM7}OY?_WGH=MjCnkFZ_ z(n<#+rtRTq?OE`<@KW9B@;Pe90pcT%ANo8bNKO3^&Hu^;AuB@ryX*A=+^!DQZK;J& zP|v_GOdAFi17TPD2KorN>Ie{#%XKK-$*)#O4$%9cPOM_|t{qU0q{1^$-CqP66t zx}ER20&s%quRF;d>L|_6TtMgJHA(g~|NXCTx6OIwW>Sq4Eqn+rX~IE*6yusMrvsTx zI>wh4GreuR%x&(QO=58sYCU)VyMielFePc!LQ8Se((V9gB3URA+K zBrSAiP<5fpH_4`38%@+g-z-m~llA0f>{_E68fU(*+oCw*j=bwwwHlAqz_MH2#}qFg z&24*Q+#(om!jtog^l2=&Eq?z#j~Kc|W9m{dD8Lvi=2I+0M0pE@{?}%ZxF&5jC7J@y zIEn`WNZ3SH^sSu1*(rK02<|H;?Mzs9Xp4j);D#@gC{FPfL5_Bk)Z9$akRjl~9DWBC zoRr?gGOHfo(TJN}I`*X{U$p#?(*jimSxt^-y<`L&QW0d3D2X7!F*~Vf+0&Aueu+L35>XQ(7I{VC9;H@y9zmvLs({_3c8 zOq7HTDdjDHTe@y_^Ibc%W4gZQ7^^Q2dOTaLwRA|QHWVL)HjFyykd@Ttu6A51D{M!U zqyQ>@_Ex@8S_$E$nodh5z@_j_ET`|-#reKB3)v(pq>fpWFStG^`54X+G zW=DMF7-w4j z280wCQ$4Qk$VLalkvD(GrrFxaKDK{ArU~)J6grEgLfyI6?HrfV<+)dsu||^t_u>8o zwG=40i0|XD$`gGn*ydD%*fhOmv7oCJ(rS-=7?3tYxoCY(+r_|RXp6=1hpfWr=`$T-=@8(49eOZ!V%&$bK;f$JDJ96xu)yR25PD>oyt+XF4r?gc&_z#o>INNgOo`TGYgy@4zS120q!k z9pma(lj;{gX@idX1gZ^P9l| zkqE?A6wKGZF)cO_5m~T2trbBOg6^R;y?mFTVlK|=5&vRWR|H|heg-44jtZrVwV(x z498%v`OwIF{F*g1-BLd|vKz5X{c3Zya#vMkP8~XrN!|x_&nfz3@ibxeen@ZvOS z~2rE=AF> zi^w5ve=fo+ZZYtp&7-nSllqqFy`%=r8!kH&uyzE#!pV-C#ThHoP7<%Xzhrr`Gd?g zd9$4@)lc{K{G4$cOp_P+I}z7keO(|yrQ9jMU4CZzcB8C}`lSGat&3!13e9`P_MRyE zxwa@q=;6Y){n+bsVa+8BYSn@{e0nWf(B^R!{SXebEYq_t19Y# zwX4c9>{k8%*{*{4_&5^=v!c&HtoRJm-h-kHEt=@|7a!To!h`Jc9pl}i8N(>Sm077P zD1OG^&a!JTS8(@`AI3I8?OH#Lx&0fqZYj<=Vzhk>+M0HZv|b>#(McU_?(ZA6#@Cm~vYI8vbAa)>|C z;w@!RHaV!DAQw~*qijPu*|D|}!+De_d#$rbHwOq2sTWs4Cw-F(izPo*5Y;PF0vx#P zOV?y12E&wwEt1xqN~zCx%%uaY z_#CI=DAykT#dWF~-*+D4I7izA4Vb#8hm!Scr!J>0rbM-m~)~i=G}vbkEHXEa4c8zMSSRYs2$R({hjr!xfz_?;eT7UNkY54$$FqM2Yhe z9D}5>QmhHgTAjZDenhKPT_RN8wBa$cWjgS=3GspwTW|uwgA)iJRqDv4GH{|AO&m^q z;chH?W!vZsMZrBgw5l)u^BOK5vV49_FQWTE%@*Bvl2GHG`1Sz8W9@0WZ| z>CyQhgY;x3pYwaKJo)}`mi=qAp-P!EIV z4ZNZ{&iwLlFIk8au)dgp^|fCu+D??dHkIJ4$_u;C;8tCX%$NuX*ypLsFMT2QTYvgO zL9dqN&SAb#n=L{n)EBY=z7V7RPhZHa4%ZCk3*}t+La~bXsfH{kI-Om}=I<}H<(uiM zpyC(cD?U~qW?#=u;(Akb>(zwO#~;{xMvG+%96^;6dyIF)V>(yMH;x-Ajx~~u$8Zf? z2AOey#KaZTD7i{28)IKc|8BS6v$Ti1*EtIB#s2OpgmoLlCxQ3T)(>iu+MSu6-o~?n z+8x!4K#ko>aIOnY3UIq4*3j`_%$s~|1%l`^jXq(L@f1Ov`OF!G;Ep` zgZkjgOWlcC-ubigXoMW`XKB$0rqp_b@csbg=Gzi~rcV+4nm~seJc{Rxx1|)$)UL{= zA0J)`qvQr+rSvwvl=6(16%rvsa%9JGq-64hT68bl%_kvrC_|-m&HHwYcEg4-w3C?< zgo!yDbgN81hiYTM3Zl;OF*Arc9J4q+@I115pGX-_R`Y#0^+s`cbJyQr~XBW_!_^8nVlFNl#qblO+|G7Z+%ezqtXEFg`@w2(g3Sh z$Z?b>m5Eth@Vj-UmrlDGuB%mb?IU9DSGRsU@XB(A2I~ZNfrdG-+h&H|o29|zxnC06 z%$C@iFoQZ?F5KLwI7g5_h0eCLwEc~er2y?1qqGu~i~|cE^h04KS9=dApo!xEGRM{x z^anXA988rSt*zZUU_jp4VI%$6%Gl&AHDa&au4($#qO~f7`!EjY(PtJ^`t{R~mc)$P z4NR!SvkFD3T|FZZJxg@rovqOI*&}=~;<9!+e!LAGCJsp7`lU1y^Ly_Ts5B`W_-ARd z_qo;>)-1J0p#bXkn0B0d9&cU8HA6v@{9>gteoU@O%y`1<}a9i4G!mqYSA8!IGTe~39 z{V(VCCF(^(>*FiF?@yUEGCfLV%D#0uJI<`!P)rb7MUS>$Xx8$MUtr?7=Pc;3WQC8@ zua|GPkWfG|oSPPuL>k0jO|pQ*i|qC>?n(3#%T=lnDD8TAR#6evXNhdY?QxV2j|Wc5dNI9AyHR?8k7TPBfkN_TkJ{FGt~R3%%j< zh?iTVNY$ruA~t*IM1%w zPg1(?f9@4jY=CwfzCZ075Z3sX1zGRV?zJ=TA2xf{L+*N9lbSvXgT`LLL;ENMZ$t;NvjjT?I z6MCFLMC3_!&?g8|j$^UX6)cDYx2L#1^V5AaYL+WS)qamgAI8P6c>t3 zt(&0mla(IO=BECKDcV*H3%bzGW~dCDAa8)R7U+66oD|rOHIpValWsc8*8o)RacMh$ z1C~+7Ooc<%y9Z3C3ITxR3GfG>Tr|d@JrGl;_fLLk zRGa&bL;%nbi>8wPJ8WRTPaz4fSFdmq818;T)|F0-;)ppml9?kHGx`Y1PrXnVk1D{9 zvbZ-)WzQLJ@If0lXJ(0rp)J0{)ZkQoI)pohu@v`g+IwjjeeG4x_=JY-aC!kbr>3>b zn$)*!tnGCSg(d7l(<%fimNuWEn@!)bMQ5n{qzQ-EaHkL!Wz(s*rd=vk7Odpu(^v6 z`^lt{$dYfgvP{6UqBbW1&;6nC{k6#Eq{jBw%66r-g82;`DEJ&dSL%VKAruo;dip&Uir^V<#9{)Q*hk%? zePo3MmJMyZg{>HvaJq`hB`|JwrpIZ6HHi@P9h2I*mlP~x3O>;Oz;SOz3VctP08hBf zdvJs=gqJH76dmAov%0W5C=PYg`??}sLaSUCi6#F7uGi0!p2U0quARc?&g|d*sh!4o z{!u$oq{Fn++F^jv{YY6hvCztH#rT5--=Jq*Ty-sv1FD0RxH}VTdGpKl`OVcKD>PEd z+;x+dS$bUgkiyC?E;;q#+BGKe=o~$)uH$L_H~K(xjhY*gEIxb56K2F6vBAv{Txz@h zWkOWVu&F`tR z-+4=%rwU%buR4yGE#b;TY%Y%GK9hnv&fGjJtyS(-8A#Xa<8mh7qA^DA<5w42?EKAW zk?GdjoG$c@sw030GKh&AE>I}rL(cG%_QvCo$GoW#$M6GFyd-x-+sbrF>F_H4sGp%w z8e>+^fzPI})$%D2JKhlI%s)Fi(rlOsFVPl^pVHG6(N1`Hil`E2UYO6geXN^2uYrOq z8B@h=)w7W-e8v|BB@zKWC1V9DQlRf3g&KM{>MoczD<%P(Ouoi}W{2wSq%06B4^4AK zV04G(-{2Ht($&u-%zbkxpABfB9H@>TNnZ4LgMpdk`O74Yi6tavvwGtgpegA!DAz-w z_qh~}w*SHfTH4l3;<~+r9lEr1US9F5)*3C9(c1n;tuaWq(CWjBaMdwv98;f8IBJ`p72ug(;)tR;{yQ1w=Wwb)Qsu~KN?{^gD;tq=Ap@1G3w%#AkQ&5++13<84?^p39XASbCf4OF~OSEr%WYh?w55YCNbsZfc_7H)yh zPr7m7Xm1$ICpUecPjob~mbzKB zMy$I#2H(IPRwfJg@kJ0zTn8nzQTh36hD`r{`kqjIqs6%hBxQ9tBjOdP#(HxkQ0kPa zINHa(%sC!yt?{bFK?5k@Va)c@|mA;f7TEy zu}$&s1Y@}y_{Vu`2;YKaF%6Smn4FXB$^>-6Mqw5YABFaf+LsIJL!L~d;|6nVX&9C% z*yyWhP;F-`Z$HbvMzynfV(0mfH1)2l2$+bijkI)Bhd{1}IRx7xz+Ln{Y;#PWf!cht zBUQ7ndsfpPS_k8qeEWwH4_No=>x36uAS+SKmsU_*{qI%~(XUoe=(dcCBp;_AAt+oa zI&|lmVv$WlcQwRryFCSmS{iG0{zjUAPsEUH{wrB_q`VLUXi#qt4bgQ%2|w5$zGC;# zi>9BMq9d_T&Q3p%Pce?y=)~0C_`8}{+8jY5MUUVj)*Q*Mlw>Z9uTEoC<^{_aBYzta%YML&`o9~5A$$Hg2s74(Uf9&b!3S*2U@Rd2!U?)_drg=| zuM_~j$T#^ZVgdG_aA1H)R9`kdpy#1o2JLG3G>35+KnuEtpr#gboKMl1sQxmm6~ZJ^ zY`oBhi{e~(M<+HpY1IB`4}`|yBPw6z{EjpzuG58tk=EWmI|dL7&AgGJPb%rxHXe^o;TYbI zGc`HQ771w}n<${PsrIp9jELM*(BCr(Ys*AoZF&)!A*uxGa-Yd+d`}ZMav{?T^q{>h zroZ?i_d0CGHBnE>Fj%fH16sZBDz@E4QxuY~u&^$rmJHbmH#w+o;yULQ@6H>l6)Ota z^{iVWUH(^)C`IET_)K4_#%?25|1f&dqUmDy(N)WSO4Iy-NVLK>gSW!|x6{T>a_lR0 z+#Hv4F3k@F-%y9nmIw|iTCwFsu_(bm=?R9e%h{%8SWR%LM#S$fD8Q@HOWKMrUmv}03!nE&Yx_G0#9qsyVzctP{F zxFjBFfmXP|9hbP_r^sa@Gj^n9ZUQtDSnfbXrHmUA>2}TkUj|zQ&A$z{2IjE;*s&uk zGOfT!#-}Nilf{{7OE8;ub>Z&Af<;WxXc5kM7^5u?wcjX?iZs#yejR!9Q%XyGfhRWj zbJ(l26lB{55VCMvtiDOu@>1OOXZEwacd2Vu@BB(>|2p{kw_U>jL=b5bu9TMH$l=KP zii0V@T(3}WfRRW(Y@IE+KT(|SGTYcpobu?*!pgeTICHah1^xuq0ENveK5V_JmBR%i5%ci(g3up!}tiQTb zr@=+7ryndV;YokIF~e&Xb~^ZHYl?5e%DdWRZ)|M&XwNX*E~$3x&PaPR#tw;UjfvWv zg$YawNyqep&~Psvc92`<>(kzRN%W7=AbFqA;LBG-YPrc>Qo&yDbCa=eW}tWjuQ8v2*JVqZZTJO|Q-?56hvSLqqL1 zGKo_dN+vaGK=Wo>zvsJ@fAW7dv_heCR-F4nphNSs%`o+IA(mD6@SlYV+!2!b@5r(l~|<(vx$r`4qre+3TP z&HaW?qrwLT%XxT0_WXE8XDZK5hYvLj*?ga9clHxGUlMmch~XW$J$#qv642sZhBgb<0kg&wk@AtM&}PD zs%OqRF7oki*?QCVlkFi%@$7uL*DSLWMQl}}IKZ=I9MiPWQ**-IP@9_>+u5u%O)s;t zG|j`?Hl}a{c(UH>J1VypzG$F-MoP?DbX(#EP(-OAZ2RCLLDs6Iv(Axp`Y?=@or$ za*Cy@Qj)wIvU;~Xp_o5S8KLcX1P(T2+J>&&Og~7UyFc|)*@su8c2iu>(w!%fKZCu= zgAcS!kXatj4;M$Ch#yEy`?U+WVfH)OqBurmKHCuN#SpY1G|n_yTEA~P$Mw& zY#7*O-sOkMd8xMnf?##uuO0;4>_9RfoK9TH-$9<}84a(T%;a6%`y3?z_c5VjO&84Q`)}S2 zeWI}w>%SNkY1}n2-S_UW#$@qRdby;Ch%LSEMUKzQE%20U=FH#fg`#L>LT1KqMjkQs zg^?#fGwj}@ifi3+KSTn+|E2v#?-qRZoFr?}U9X2kF8FSTD8OJXuvg}X#VenCu)jSmJfZeATX^`P>>C_d4<2{kw}0N%UUYUst%u!l zI5Ix(_@%_`Ks`@DS5~WQvs{duuNuP6yhj4}dF`&seA4{v23UYIADDJa-&Nq->Y>c} zhg_g$=T^;~*LUfy4Od;4X%)b>*y@ak3QmQ!mnSOcahp5natWXTjU~244P9s#DX|>; zk&*#p@2vE_elKGv9Rkx*V3&)34Z@4U6}>l+*MFLUhIa>_i}Gf}1g^$cxPYEm0C8I2 zGYUE3cRb)0zu0+$yc~NQ^|@#Ts~hCliZiOBZLwCN&Aq8K8d+g8b&$COYhJHb_ff_ZL*c(k8L;=K z^XM+P@(8b;sy=yi2>0zB|FzCo{*s$C1Y^d*AihR_T!AleFoS8VFFAB5`JqRm7G)k4 zKbBaoUk9WADj-m!1mqkdin8{BPuR+ucI39GXOGmBt!EKD?8H`sTj+0wc@&b#q)YAG%7=+1_9)tH9kN>2e9{P=sZtR?Gs-BiuhdR# zfO?7#xSLyOB@>7g9l>eqzP^-{>LLX_*E456p!Nkt*mtu&o3@lh)gI+6=EY0d03-_I zHSnCMsv~gQ(b35#5O4b@N#^<|Np^ZIb_mCGQGPuaUyD=QifT^w&}Z zy5qbuy*ye|Vd4ghsL{m7R}U5C7wfptMDH((D_3kRX~jpT3o9E&{t&ztw9Nn#wdP^d zV(_af1c_`?UY2ER)!rZ%d6e~6I82yf!QA1;16*6&AZO`A(;)xrCFA+R<0jO-W-`R6 z90<*tX025Tov29no}TXOgxgzgxXfshwz;@hj2_6ZEPYP_d$iLPJK)5KSnn1yiqA*` z8{FIeGjl@xPL?ofP5G=J*qoWNEg82+tFXwFS7ad(5s65@7N?moZ4Ns(py~u@^6$vi z?j}H!3))|3@&%5cnjHJ}g(g4yizeSEI^dl7QjwsvTqgL+iGLll6l#%TR2Ma!Vx|Kx>FH(JOZLDi#wS>xa4S`o;5t z92Sk9%Ns%H4LeHPT{?GbC&F~99McVwyN@y=jN+i@+gBCbUnIXs1@`XkX^M@LR7=Pg z6EvOU+Ud?0thL`ty;t`?uU+#CwE9t91-brgIjC9ODR4m@1y@+3WQxwfH5Vq2Emfh9 z0>Hn#r?=4Ou-z2sxG>6W_m#O^I$!HMdM{Y?*NBHxFUlBMZkrpvmp^C^kc4V47}AEi z0XAlLZG!b$yBqmPrK*7>m5N8wMgd-B8K1${OZtYWytt`I%d!&fb?Dp9ntV_PgDt?{ zW{f;1P6~Y#0WXjR{e_VGr!|BJ8h@Cj_ukk}OrT`>-xWzKSR~qBN*ZZpO;HggkAGB+ zAsWX!Nk`xG#22-`(GKL=xj*@#5{VtiCX+9`V7 zHF7^p;K&9i#cML1+Dl(@aq@rR;xG~amLZ@MlNk!6B@Qwn7*%?6nIUj`{>J~g6cRuU z|Hu#&_;8h_RUI}^7vQe5@z`_EnG5uc(sJc1YS-?NBsg$4hZ}_)UCAImd>*BH_pm#t zoBMIg3E~^s+Ix;wm6^Bw43^>@xVD^IA)1jg!Zq6j9ueIz`VGS82+YtI8MDxMrd=8x z>$Yl9gXGNi;p|Y!eo7EH3mw#N%-F4gNmUCAs#D?bPH4@n%Oi2DLl^DZ?5N8AKt?sJn*nib0u?QYC#vj;` zUXl{VNHYGJrO@uK_owLNslFN7&B8ti-#uW+Wf<7_7p%*E6JVwL*~hI{P=YuM<|c=f zFbj;Le-948OI!vA`u-Xmum-__bgCI2{;T33IpD}lL(1u(r2G9)Kjg;kjK?9h-Be;p zYutBw?yfF1FN$MjMasj65O%5Gokylr+}Ak4SX_}*+0xy`6Ul!axvZY}!(~lY;hu!j zPkWQAb7yAwtZpt&rrplf_4!zSV2Y;tWH&Ld{J77<9*Cdqcx-W8#$bKj*|%W3<+9oQ%h+B!=OJAA9y9&3}nJq`u(b zPOhnrj}WmxHQ%A7|2=Hkb{V$JB+0r(Cj!d@z>)DpiZyXzwZulw;U!v~mPH1*(kxK4 z>2Bi8+D?ojs+o()-)o%cem1Bs_NlVEhj&tv4C;AS*JX+STcq-l{^|1Zh7K4h7XHXT zV1*8Ve^7V>=HjZ`_L|^ zG)5-Xasx?&Rc6&$TvNA#)_0Pd>pP-Gu;6-Hwp$BZR`vDAg_j!H}cFXz#V3iS^Qx}yH zDL`HIZX<(DOATi|w3ND;NM6`9@zA`<{&!(n{};twg)r)E8A`oXO$20b*jt275B=xj zh&pKNB4n>N=7(Z?5y+~}FaO>e;c9$Ay~T$m1$YDMO;qEOdh`B+dOHTx+r#@G>5YV0 z-XfT?5BDIxgKuEou*$P`>z0C7{<#wI6i)PsdUb*W)qR#wv8F!|{bJQiL?3zfCh#U1 zL#>wjS@}WHJ@wBDi3OoB+z7Ijyc?4ctI_-%w481OSmd$1|2gj<)<|<%Nzd>GV)8`U zQNq3%(++6($#ERDz$htCW03VK8bj>xtnE1|)L;O2GgP7nmQu6aKowc)n2NkDEWm>eXNN4wK1jL9gP_m%7J;L0C%* zOf5VE{Qbpi0ILP8thsolP}2ZB1G*Vw0+@k8dJiZ_(#k=B;Gl^a{ePOL&b<<)=&J3` znqQN-9Uf6-u7jalsBvQ>_Hv$@Kl0~1^$6o%>3U#MIbO$s()IXS|0}xQ5;-2+*hOZp zHIE8gl+~rEwbqoLvurlP`OuYgx?AK5F;#d73G%J|# z-^hp3Ui7D zySp3+*tT}efLfB;Dlsmup{*zftJU({|3N?;af-auDhqj3EyoisQ%FDAEZ9j*9_>^D76!cQEo4H|Tt4n;9qtUa)#vi&HA`8~v2Gqwh zLe~S(CtR!tFfx?}C$F6FVdd#lImRb&Y0Tkod{Bmh-DaqJg0h6>C9X&4E_=#BcbU=o z)pAv*`C@|&e0<}jPZD14fiO}yGuJ|a!|UEebjIbL#2J>;c1zETEaATA`zPv3?UyAC zhS7i>pCU*Wn>p>+#JOrx)B9uUP!Vu`d3CmZRyi?jTV3FyRiULE11XCV+)&2?LBoA4 z2L6cAQl$vHwf;42`G`!R1l&bC7>3V0M{D)rHKbN^ug_B_2BNhb)khU& zj{IZXgRlmCl?+kHjh_I$n@mY9XPH~!k8)Otvv5iSr9(3lCMa8`TvP5?sx`}^ScZoK zN*drNBvr#osc`IW)6@XVwSiY-Xifv&@5Z&B`auyxIF*W(|1>!L@-Kr^+RMS|WbJQ* z)9NyCUl^Ko&a9*-JK&}+kVoI({y8{($~(o|EttsjK`nD(kxBb59hP1K^=$yCpDVls>L=YiWYvBH>PfJ;uMa6= zL!usVnn*>dJ(mZS^un`5ICFgIT^-JeER0=xkzT7rChz0b4&RfQgIL*?%F+`iGA5Dn=7<%K%jinN(j>l0ntNz$mn8 z!P=(y%g-EO^BKqi%H8{;g0zsOZ zd0sLTtWUz3RAm^Advcm>E8x~taRo+jd5Na`Z0H5m<_@NCM_!mSNM&!4Akhio8$DFL zi8*L=J3spsU}9si)yE7EQ1um2`;-5J+7Jgu?L+68ciwwF)V!PcD|)V1#(+t=!TzeJ z4|qoo1gM-EIypr!3uf8+-U5`8D=qz{p$arA#ASg-1yR7AX~RNo!XmmqSMD#VPI zt4C`xAXg!O$#gki^7ZGZzc5|ZqM&Jf7Z^|AsjWEk00Fw8eK*|k5&eO1QWb=k*+0;% zng78(%TLzC)diG^7{MZ01-hk5R}@6QLT7+t9QP82`CwRA3tw{9TF1JS@q1~eQkl&L z=3#?TMBTM=(0a+EId{b%LAlOR_>$q*-liP6AamVqswW8{BGp zcNV<3L8R&7-D2{bzH3pJ?TR(hMqR!|OzFasU(Qf9DkK8G0`&jt#)8wzEd9?SOy=LH z`qHew7GdJkh7Pf{`y%V1c(LcFToz$sYf|D9DRTZ=gc(I1ICcHoqu)@k{!$s)f72$z z7(jf;^5T;99kFG=Q?XpxuV1+SZop}=F_-srQsG!P2Qur{H*a6%YcW=Hrc}H0Ip|WV zSl0b1RZ3JMVjLO9XSuh2oSe&4W@ZwMWrIQgN<6No!!$J8Q{3ZmrWL01gfT7j9(Io& zmvOAMTiI*+g}>(hhEZjC9>MduD)6p%+Ql<_P1*%2HUkyxYncOUN9g+5j(}(Ow{`?< z%yt^Y-B+^z-j1+gp8Z=p0_Pv?2-|;YM_A{@XG>6rwj-2+b_Dv%@m|_r$9pcuT)Ij! zHaS5ITkxQO2`rr<47c^Z+9_-XIVwwM*7aWr1D$)nt+^N92#4nm8fiFd}x)yr`Do2@n_h317_V2Wj) zi|?<{{3!?N)xRm0%3l;q`sm6NMhH3`Y)#N$=|nl#LvnEvs>SSl#nSW;-3hzlv(x2C zd*ZRZy~b&6eQd~e9dzQbMJ>BRRoluz~Fk@3dA zknt;E9xEckDGfd%v{-8WI777p9vRN>L}ZtI6u|LWBbRWzqU^U7Y8V_}`v0`|oncLF z+uB&bf`Ao41S}M#E7IFmY;@^{DpEp^lz@~F6-7}H3=le@BcVwz0U{zGEukYFr4tfL z2!Vvax6pmg-uIky?{mLj-=8mk2+7Jy=2&Bn`ObHY@vfxb#qoz|f6Hx|b6OZ}d3kqn zU6x;R(jJTaErFoa77R9DywLGr*cEd(pm?wR%kLpj{w4NP_DcDKWG6tMvJz(Ye<64f z@R#60_bs4KeD0eS^}*UlZb|g3+}o>S*H09@i~RnHo$0~g26KSYu^X?+gKZh#O;2!A z*?6-VpH0N&$Bphq`etP8`?yp(U{{=dh7GzcYi0%)IWetwn*Uo|Z^;r$)c&%pDJai~ zRs3Bs(xurbAGbEyjZ{1;UHgYyB_KA{l+rV=@!@qB<hv{=u7x-ir>o|ft3(w9Mt)c${GkzD9k4RMxk`? zH4f%19gO-D*K}L%;hGWq(TT%7qj~T8G?nBI-fU%6Xq-q2l;>r>_|K0dxVLB{1Ps<;7B zB46{i+x6v23F!5`BTm4${@*tizH9D8Q(vVOj*zA;{}ScA_opZ)1oM}+clV`#E7|%- zL!m#=Q25}=UPIxNs{d6(;b?8yVZ~gk`&?R7fOMGd8sD4D@dLR!6{gu*+p1>so5&YU z1`dTd9rcukUQiDUO$%koorCz1GE3Yinu)_qd zru@O08dMv0?wOH8E)sr{Cw=k3!xH3|I_`?`sJ(lL{ucViM$G>y!Rh#4Bsd)##Q#<3 z+YG=rwr^kACcKn^{)ulgj$34$KHn?-cYG6Iqf2oFgudPWF7*9Yn{fK{e-`>Cy=TRi zeI(BrON_gj23dA3sQrZA4Gi*IsbR0*BKNrOeDxt(R*H;MNPki=50`^RGm?@PsO=az z=$?wTGUw8As&C*9Tz&LE7PODl{#WVmssDxO)AB#0ztg|%?SIcJ?rbwZA|J5y3w|gX z^DLd+Qu5NPbDEMSC-8up%X9g=m*O~}W)k5Box|3*(IzJqPyhV}i|G)sR{4Xw&2Pb% zA`XghZNx7k{-L<}py)p+ZrZ&76gL|(|Dm`^{8MrB#=k0V>Ro(bro1+13NNW(WirEo zQv4?u)T~x>$z2Ru>C+j!XtGXcP5%eIz6a+ha7Rj^FKFi<)$B0453%Lwj@sdOHRE+t zWWzyEF0uYd5wq0=E`0NW=DnIGbcs7-h%)J}g1a0Co)I2 zuP}9F5 z8J_EWcHe_zy`nzu`xwKL$boJYC#%r9z;|tGL)oNyz68F507)ERufW}Mz~>)|nI`{8 z;*bV&e>MAE`erS~x5WyA|JMD!un$Nygcgx$tR9ex54c~&>wN*Aw#9|`>3M;Y3U8>}7O%|0SdIKPg~-v;Io~vrh;3xqB^q5_~dK z_GN#z@Rf7?rF8J@`D`TPx6(lw(=Vk1dqC;H&HJB92V-Y`D;*^LsetL0({#FKM|<`8 zvHf9CCxa&8cJ*jVPP!)8@Rw5fSa($Vc)@QPhW=wu^3slY$7K2Wx1ErOhGD0BTccMs z8jT+GxtX7LYdx8%Y(HFA_yniM(Q}<}WBG*G^!B5ot$Q}J0u{{X7cOgVB&dy&rTCVC z+kIQq?kQzE-u#{O8H-GP_jE7kWArCrasFq{r}596&!d0I`9yla30i;Ue0cvu&WExK zS>92fk|!%iz-NoZPN}miyyH)|eo ze93m{8y9Qu=Jz9E_lNV7f=`4!*@;VWO%ijpJ?q37<^d+Y(ijk`7iBm9;B@V#>*pg% z@J@!l@tASHv7T{k%^3_M==p`hoW?7qg%WA zY;n1&{nPlit|Z-o_7pNn>pldZCmoq)@p=IvdCIzPSp8fXmQ3nQWGuK&A7f&YvQT?y zZcd-tAdWP)>|?6BR|Rn+h(0^Q#KaUKM@Nuvjo@veIHX^L?pVmmds)$g8)(rpg623-dNAe#>R z>O2&Z&Wk>=bgYJ62+y(s#(x?7@g@pqMDdl)JP^S)&7*}ei9#dA^3dt7{kXDdQOPZ2 z@}`4o6~_lkJi@ucw-%jl$;y2CoT87mqD{d^MC{UV{MScXE@^2R%c`PhX8QXqzVsw~ zvC~HsG&hlgZ{~GJ8})U2N*JbSAP9U7Y@XT*dbgZ$Tl_E0%&v6i#urtUYN18Yxz(S8 zb>PJKJ~$%NL1*wi3*8b#07v%S%NEq7aKkYVpWCai4hmL+`{ML2lWM+t3^=)H#ncuI z7f-()*bI7uo|tW_%(+}bYW5(8@?o-N-w~)rQ;I)>G=OQK)9-pFZ=4|}9^-X&8Qf!z zL>5D$p!AFKNNwW68;N(THM3|T6O$zR4WY81*>Yy9(EHA%RRlzv!ZA5Dnvs8<(t-Kt zdd>26!t6cNe1Xc&kh&g$TaLK>SS8YDarSKP8sMd$>uf*XnRW=_;9=g|fgry#0 z(KwO9nRisVF~5+ub0W>sv=O-$V>&8$fmw%BP=;qr4d>>xb{nKHohW#6Xp z`x7efq%XRk8%&8SPWyG}j6XzH29ZBiKYz9BWvQxoB7FAhTHXQ{UAWJ&H1pgj%vLG;^ks;kHg7%o-w z`$Nt>s8{8Y=Yr34`{^K4Rve?g--|B7Wmg575R`Zb(S&X8+M7e`{zgvmQ@ zOo(iI&$4A5RZEbNJ$Uzo{k3&B?0(zUu9WxVB?1)n{CwzrsHL`o+~u+y{8|)gwY+_J znU#exAFTFLBCKL=3rzX3Z_fVc^YFAm34#|i!q;JDKNHiV%wsb18Nw)bZNXiurIZI& z2K6O9eo{U`w2^I@RLj8*i(cvmYF*33b0$Nirv0^9cy=_(oA0FT2XMYkpURg47o2Cj zxLhW!X+!_yav}rgidfew-VPZ~fxVZ`5gR)+az7-9>xhP%Y--SQy9smeG1$m5xPwQ_ zUa$-dgoQUzjti7Gb6Q{Rj(%oLvZZz}(DiQLCCa|1yL7O4;51ncLM5n7=BP zO=jLJ=$gC=?0b?VR$VL9(_V+&sp!@AS9c8q%uY}ZBqz9e-pW<-E1PQ6f$A{oT>K{>!qxYPq*>y#l&U4!aup6R11_}ub zhqqa7Awes{X+uv8_D_-dqOv>FX?4rMjHPJ@XoVeXZw;fq>*)R5RS8=S7t~AG2B|LhM1{W zH84(&HsfPuP?4vWeywBY<F_8Qt z_4W=o#n0TZ7T5IY1O=r!r-~gY!W>Po&b_icUIa)kls+WdmVSTg!2n<1q$ymYUn%5w zSbSb)kek_Kq~lZnv*O)CO=krd1(ka*ZR%BM{#qCGcyY3x_i6>dr+xCrX(hjq7O?C! zd+y0b=(FNZZ3bx`Zjq?g`^rzNKOL?BlETl^FnKZ$iOiZ z#nqn&(AX)7Mt`zW-BRMhRNZsJJ^t-9jBq4R$C3K#_MxhwfZQ)G*e9}(jhxaC(m*_N z+-eve9_FuvOz^3)GyQRwWIjSW`Uvn&Ca$voH@L&*8?6q4or_4v)UlNyB?(a9M(`=$ zoF&_%3rW^nF?`BDcDC0Lk~3O5n3mlhz%!m|T5o&FO`O7ZW2|56afSK9w?uW|CsY%b zvMGt~TPF3#gn7J2W*Sa0CNSm4cj?3+-2+S)3jQ0+B31QcCo^!;w{WcH**6oB{|#h` zhw^`Yz4AH%CT>4c#R1zveF$JW)_+ZhF%0cI7=(K2ckEM9$@{E67~$M1e%54y!xxKj z(_T{9RHG1!W7mljd@H;#qxKf7p2`;XdA#I~;qLPf6BU$FRQ>dHz!I3Y)%e4!-a0^2 zt+x_5s62S&i^h>!A3u#8zN0x!F5=N;T?ijNd8}z2d}7&&iD}&hSYz-mMQZ7Fh{f2R zoqa||{hlw*BkfxgBwy-%=5(}n{f0D3I^Vw`>G=5b&9ZlF<|3TN(ZEuC^Ulem36)}? zfm+()5y=am4D`nfvslAMPyJekhmJfoGVXYQ9r%(2tS9|94{8IWa)l|;P&$)V8y}IP z5za*nKiZI@HDunmZJ~&tRd^J8l#CSO5!C5VKI{-sw1gwBy2>%J~6s61=hw#FT|_Fn|&H zQ*=ITv)Q>^d~9;lrNI}!Lte7n=~F6KDW^_xaK;&IOon#p?fpu6v0 z!7GF&E9=UD2ur0XE>|b<8#fllz**_F2?_6>u24j^IkUQsMVe$> z&Hb1$W09>KB`~P+g8qln%yvw{Y|?JT5>mSTlJx?vh=ja^hai1HNZvqP*V_@jaAc;5y3UMDu~JTOl-~p*`J*iP+H`zbzT_hL zIK*N~f&9GyzuE4@jAV=N@mJU<dgpEsKG6lKPsWc=%)O2*curDV7D&GD9Nh zMIKuZ+twRG@0;cRNY{qBY`gmV6UH?+lg|uR=Bg&1plbCL;vLZrc@HThuv2c5mY-VG zcuCnYSfW6oWm4Iu^3-CiE*AP`H$StR7mOq;&BvWx3gef2Kks?2NP4^QU7mGM+#8HU zZjw5c5)zc-=6^kHL}_2*M1^2E#Wd|vI|f^uIpL?(`o1pHkaqp}n`)0$)Q#?-+R~*( zIV3zH?J0JPIOkvTQ@q-luso{r23NGL2~i154Zs#jW{sle-;blAV_6q2XWt^V12O&*HbGmWC?STz!uJ@ZV?3brMi?Loc4-k60NX@56Ut9km za8)fgn8d%j)f&9Et1{tyBWpX8{7G3=g89YG9a&c3LS8{zeo-tnGk7nJ;oNPpzdgKJ zkT`1~ws8S^$%7Ocg^bTMYbiIAd*ll>AQ%5s)iRYD}CwC4v8Sa+#!%T*{M zcHC3weQDeTl#H1ooOP!$sbex}y8(syg6)X%=wf1eAWQ@9_tVt>(?*u}3RalZK1GZo z6ZO0hp0s;%-`9wJ89irr^DFCH7vM2FWnMe@9KL=l`YsgQt-Suxt1wdqUaS>>-4g0Q zmWX1MVZdEp5pKQV8zGsJ6c?o&xHmZ}6BX-Ov)Z&vv~rxpljE`9G^`;n^%k^j#(M&9 z)<8SC)yA>Q)V-i7^EV9KE6k9~JmGE`CEi-71ATGdOT}vf0{~yh869aZ3z8fOr#HN? z?g@TY#ZgP@h{Row3G-{#G^Xy9WxP15a=i@92|zFe?MgFn;n@9nwm5}mlmp5PDu#~V zm`n1BN4H9tV4m-5lov&Pyt0hC`O}; zC-0v~1m7!8*R%pQZb&)7Fu~opCmX%3EQqVP*?h^|VZ zVj@Ba-$5Z1v4hk^^?m4a+1JC)-h(JaHG{;|8DR#NXAN;S{`2~}x2GIIH9=X3B$T*7 zwc&pB>C2TgNchj;rbuwh1w!m>(1A(uWHC{j+OHbtz#hja(i{2%gph?F2WXX_dFo`Z zo|g|Dqk|s=mWJ1&@+lgJAMRs%R&Wh)kgcYgimX6m`UQD>N?O+K>Jp*XW$)FNRc#b` z+Ouc0)hc;fk_0bB9XgP^2htxsg$N)*41MQ*nC@ZpRsf?9Z>vAC+l@b#7R2twe`nPJ zZe&u!!G}}-iS|4soIJFjEhqZ9DlH|G8*Ejbaj*xlX_2waxr*i%Rz>;v!No;%H0qnO zmA2b#=2q}Yl4rt8*JEw^@6|F>j-yAG%H6DQ8~RCGx3sa4*?odD@}e=L_BLB@M=C?gMys0b+biBBh{;nb+#s@g&8rB}mP$}z zepbxewH=VL)7mkQ=EPEOa9WHqGKnoY23CZ8c5%HxV>ZZleRt@_u9ZlKIhvEvo_))W z*AL))gfw70oud-j%2NcnmwUEzx-EV{=KWcKo{V3dd&_sKb#QEiuYaxh0FC@^w-4k_ z8E8DjQtB$!jNcs?s;w@qCA=huNNtTT%6P24x5LbB?!S%QD)0?2-CCDw2k6Ki`rr2p zD=vdF(#E|{W7uS_9UZL@z;sy#Po}t}@Of!OJq3kBb+qiN_{WppE99lyR!xA1RV=Y^4Cg#k(&;S zKVEV%Qhsx&PnLO{yTDtnAYU93>O2=>C2uge4*ntiWt1s=S6Jeqt+kmfWt%#bw;RmSNPWV=VEW@GH|&=X{U1xdb{hCEhfjkd4!GbSC}3td22?Dv6Zv61 zh7naI2f=V=f~!c5MrzW%#bl7ZhJC96HnV=uElBq2Y&7p3DL7bNaV4%$6~&4|^i(x_ z8Thp#>_O=1#sysPwo#`^@nVuK2#IWKpqGz8J3C|q&lu}mGyGWI)s>kTTW+9lrPB%J zTa3Ely%gvD#Y|sNa;RL|xtfCtcqbaTbZC*`a^2KaRp}gu`kt#;d-2xXTUxAwa5LjO zGBUA>hmTtVMvZTCI{C3t^Nyd!R@>WIrSLIxWaj|BuwS5(eUFg5Mut|eAt2}dpk;QQ zXJsvs@~g1Kx&j>}{Kr9Cmf2E0Gd#c zYWpSF)uC=ZKeP+=upcTVH{h=A=A#CPhm5`tX}pb1QxVjNb*JKtww4U*paI%BfzPSG z(h?cC@RNo}s_OTy@w4#rj)r=V6>PU@2qV==t4hMvx>`fpcExhzS^YgvnW)5{=ZbRQ zxwm;5u;paRjlR@~-LxFZP6cp$JHR9rFXBGmx2YBW+GRgdBAENMOuF%sKKSZGjjW!e zgdg6Q!9EB@X)R=?`JkOviHl)!;9OulQcqsmHo>bw@DF0y^X!Kw>=y*?l!0z3Or5#$ zA$G~7G6`O@FWO^y*1!%VYmF@&J9VP>GfnDS$z~J9ZKI-|zQkxvA*uPOWaWWT1JW+B zl0HUtu*re!R+hKZCMx;6 z+SN$om(gN9vx|d`Y|eY$MXdq6Ykj)+`3!TeACH1Bax~M<{}JBQs9-v4)?(dC4M@~| z(gmSKLhuWNq)?n^4(U379_DJfTt;I_nL(>oiLuhDlyg6;pq=deZ9?cyL|gn<*5PX6 zpx#;kk^y{mAydjKqR!~faw$zFJAwkKX&g$Iotw8OzSO&>5W=#w>uaX_zs^4wVj40_etUN z-=0v-Mvjrg1icidmc}kG^qkdAo$oBljEldm!UDqTP)`8EeB#UH+%>nIPG*DjWM?JL zNrZ-)sZNz*0>6yib7*^-Vjc9jQXtn7j>X5To&XqG+D6~CR^Fs&Ik2FqDmp!LmOiHQ zoCkL-4l3gC@a~F};NxK4mhB0cz-g}UcI`(-n)?XpDF@CxXZESt4%Lr*C{na44 zccL|N?EqZ3MU%eNxURZ+mm>m`mziMLNnTNTyY%t{aECt<)Je4$G z9jxD8gO_?Z98eP_c{S|2;M)*niPynQlGC=HV0Ky5_ z3^2`(9JYsTxA=4|zHDk=GA#fsc^ce19$d^Z|6^?l)~!GVaz&ebpIf{an;?{dPBqr0 zeVkPrpKX)Ns+4tlMkU*T6ZZQT(*u@(1LYHs2uIHVLD^r+od@GqczUK|IOWIG&IW?+ zlczi1g_;dNq%bwzh}yQ2tfFX%XnoQNW*nE$OHe)f{@QC_O9N0zpQIKyFnyi{Gyxc; z3SV`T11{C+rygenTBt8@exiNz785lINMG#E`YO&?bJha0dSbiI%I82>Fem%`-o(qW z``C_o0uLQN=y!qsgQ-gG_EMu4*&1HNb5#z@0vvGscGsZ;QKFK$X6x8AbY`_f(EZXt zrm!R+(NPQQ@M|0nrb)H4?wyM1Q!VS>AErmAPpt1_8a=Z!4f%rk?8}%_JKgwt?+t-R z|IBj#mD>(Ew;oS&ZQjboy*}kJ=r{;gRn677_zM>wtU-8Ugjf^92eh4 zEHW8x}6^{z&lmhTd$ z6l*Uh$t#=c;uohZA%smKh(BpFU&6I&_q$v{lPM}vpa7**7?pfb(oerg^+*5Ki<;Q# z7D2hErTK)hyc>0!XvJ0*!rXhpbU(vPIY>U~bf^k!?`JlgTh#uG&IQ=)rYmS?P)RNS z{(b7b_1`1KbLBO74j{Zq>&y#XdFJO`#j)*V-q#T>{w=RgBsKsUOQun! z3W52Ik%@(n%(?)8a9(c!?Z07Iq@?vcQ0qhOj0WylTJ~(M_k?E}EzDO(BP-s2WmDUd z=wm7EPSZwHbV!Iy9R3C3tZ>s{1Bwq<3NTepHVrz7`gQePUid1S{aH&+d)Lw%FoC@! zxP`+2oPiitRK(M?tF-IAF2AP>mTUDf&i|;;Y+XC)ixkdZJ-H3~Vgh7q-R9 z5N#*xC3F}Fy2*H*LF@!{rc7e~0p67dvd~Yfo=Qkg?q6EUuknZwRV4BecD#dygy!D+ z7J*2*SqmD86nCGQGKWU3>`_}YwVa5eh=ne_It6*X`JC6yWge80-KG1lBj^q?D*9cj zZjMAzjhgiG@M}c>%mC75Ota!H?-g4;jC{)G!o`Iz5YX%%4h`f0Ty(EW0+*t??0WwY zUvK}M*AGLQ!=ST4Kf>M^pxTGyk)AVxAY_)NRRic2=~x;Gafzyk;q-q7ng4!m;bl2M zqm;OSPacpjcoia4N6$BkHZ}A*a&w=a5pQ%f*vt5os3>~ct^eF9Gml;#l6rY{&HXksH@DUjq!I3_y;|Ju*epv__b_D#CCrMUG$dzhQ$uxVtyS#R_A9TKc4 z$p`7>mhX0Ha_!|&x~%t)OddzzQuUsdEi0Nlh-Bwq#xa>CjSCYKH^f=Ji1PUi+lK3V zo5Sztfc=1c^ZJjW5>kiiIbErh&Vxtrm#&(?I1mUEQ`qjCuEIPUt*tgu65sVdlkFE? ziR+|X5zR?r*PRtCaPC+3xbwR-Sy@|>)Rk%OEv3>_YtD< zs(%7k$z|_>fh=Ck<8&F@*NXJ1rKir=X4wG&OMOC(;?3j2pMS7C)*iqwOcCfe#!g-Q zkO-C{u6WT$xs`BB$%OjDZ8Ldmoum%~gZPEbrF@6jtc4~gVsB0DPge{1A*FyT(`^g( zmi$V6N5+*`7t=X&vSx-rohjbP=M+x*TU|-fY}KAZ-mAlVQR4GwfM_cP&jtTbzsgKq zUv;=>=SfI#)$nmcpSqnu7!vii7$?_CUv5ftvYEt1dj!q)McH$L*0hbWJ7tE?w>R2(8e<{&gpu$F>xeejhR!pjyRmFC}ij{I!FrG)e7ed zi$y)eJ!Mwt^l9J90kWJdt^VyLTQn#!<;jcKM6Ww84Y2nI_DetWIX|%4WGbkVW@)>~ zVguM@u{mx-ZHDE2EtB?~aMMWsz+hP+K{v9AzFNQ-UhNACw}e0)#w}tXKKN$lWaaAY zIZhy$ZlY5jTKCrYI1aLd;k>&+s;>uP1NN%{VcTVkrm91iwW{~@H!0;`_5_ocx?V-q zLAWtLZwEe>^9`~ajFU~GJO*sVRM7>jzW!@YV+!=Llq38Tniye4EuETnuox)OuhA4! z7+;-DHYvuHMC-IA%tC=c2~Pcvum4P{*mU+5aC4+t#J4llO+ zKn-+;e=5O^)Sa#!9}%86rjF~?qMH_A!z;_(UNdFLGsXGsb9v=#;ahSndywsuAP@`{ zunz$uX0Iwtmz#agPo=Emis^veTNQB0%EnWt!3jM%QQ=oXWQ!`9P1;P`J>%%Uz=@H| zSy>^a?BL!~zf1IQc3cc_n-}%vnwfEijZ0u{DzG&yC@j?63iW;-aV4An3aBG!ODui7 z2nbz1?KQivAl+eJa;7{fKf;rxs{PA$z`-oPsqxQ?gFuA=uxqrdjJWPTJxaYU0EtIR z%AXoPsMzz~a`?wcVoSWuRF=X`-d>)Plwq}OO1WrFAoO(#nN&ITX(PgI{)-d1=%R)q z)uV&&PFkH`%|6t}x;M2t$-Jg!gMyNgWkI?vZ0$A5&Gues*`9a zLwS1jH#68Oy9M%Sale4QG3ZtiG~SU8ltAta#|s}#1Bqz>v%-shORLx+AXbgyf2&A$ zxtC%;w+%$@1)};`yxi*uOncGewNZ~v1!C2wd{(8S&q2zsBP16iCABcs8H-!r_oV2} zaHIlZT-i6cv4{^Lk!14Ij<-5s_9c6Pbv>HRmpn}$18{pK6!x&N%;{EOH8|A+0_GVK2wWX}wLtMdJy c4YH$lSRCE46M2@h_c4t-y0;5&J$mtf0HSRL)c^nh literal 70643 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 literal 63752 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�>`?qaE)B}_^8ZVV`6v7TvuN}G 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 diff --git a/img/provisioning/checkNetworkIsolation1.png b/img/provisioning/checkNetworkIsolation1.png index 3ed8096aae8c0b5dae43d2b361f53df46c396fb7..69b061bdd432456b9ce3f7da056f955bd9fa0d81 100644 GIT binary patch literal 63241 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�Dy5OBwYbK_WY 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

%?xNqsD~E@? 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�&yb0L)aR7YiZ}C_r(GGBQ6?KXSWI5?@eOi1x+L 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 diff --git a/img/provisioning/enterpassword.png b/img/provisioning/enterpassword.png index 0b10e7f36c22d749ca8f3e28f1832b5d4869a9b3..fd1637e9de7abc803cab47ad70b2b48f3b0ab4ec 100644 GIT binary patch literal 15049 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 CqdJ@b literal 16248 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 diff --git a/img/provisioning/preprovision_fail.png b/img/provisioning/preprovision_fail.png new file mode 100644 index 0000000000000000000000000000000000000000..ed83c10ad25d7d26681286c633a616d8e3f1ff68 GIT binary patch literal 56434 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 a?i&!E29B4k()A;?M^aQyq*UnRm;VEb`bjnb literal 0 HcmV?d00001 diff --git a/img/provisioning/preprovision_success.png b/img/provisioning/preprovision_success.png new file mode 100644 index 0000000000000000000000000000000000000000..3e092edc0068377bda21a6412205756581c1862e GIT binary patch literal 22331 zcmdSAcQjo4|2LX*azqkA5YdU=i5~q#@1hRT5;f}R491)g(R=TrchMOw61{f@L!uik z+9<<#Hs|~Mt$WwAp0%EJ|G4+Lvy?rv_WsOgzuW6wUOV)Sh9drB%E$Na-NRQ_lGDC- z52qbC-hKEd@K+F*)gAcvhnu#d%)QDH>TTf7UpBARU*Ee|9gTPO9tU{;$XUtI?cTlT z%(uUPC~GtQzIRW5Oj+)=uD9tP(*A?)EcX0n^x_!z%cHgQDcazC;e?CKC-n`?4GJ(7(MHwY1VWd6D^fGV`B0edk2c~9HQCDxtU zvy&60srki4$FEspsd6Tf9XeMysp3z@)hxhSOl5_VJ3Bkz`uYMS_hUm%li>4OR#fSN zzRz1?wL`yryVz2deS&e?sK<=myI-*zD5#`og)UArhQoeaWq+c(xU;k>5htj8Q^kZe zope}>Zd)qj16PF@x3mOwB`Ty#I)u)MlR>+MG1j@^?aUFP>>#PBrrB>i<^#3B&4N^T zX4k;RQmebVzOdKVI&KtV>)Jn z9|7;jJ+!bd@a(;K?1hensU3t~G(G_I{twRS%{j?_O>FJvu@}=3p}NXCipxtyY`Z~N zG)0Yturk?w0dyVf?aQB99!=Sp)^Ewb9hOecjyqelP@0aZpmC>p^+LUW7b8mS6(h<9eFs@if@y>}RRP zwc<>vYVZ{nl&SU+nWbX+qPSjC<(h6utrQ1w#X%h$p)=;NW9#8sBbDYP9^gjm~VwW6cASVU0DGTw<-&7)ZsxXm+N=QK^{NdWMbNu{u8x9IH z?az|?qyBIUjIE|9;YzBO4i2XiIFoR`*e3C^+#PlDou%I`Z=F2EDt@T4 z!U9P5-afpWATuOHX?%P2@M#BWx>f*iuW-2@H6z$qYO?cAQf-A1%XnhahwBAW1_sjf zneY?$)W!x|#MHBBrF=Ub-L)BR`zk3?Ooa>sC&-$;?`d(In6JBzUf1Q0zgO9$B(CRNEBUh1&Rp%XbHeXNK*6v&n2EZ0?_ zY{Lt=kEf+7WEVF=EsA~mPhI}0da->|%FR9QGwP7SRe(8NR#SD?Nt_Gp+~EE&b6O_` zSyyq1Y9S&V6kG4oy=rp#GpYTz0b(v2$v^`&|4gqkv&WM}Ws&w;z^9BY@eTWVU;Ud?pDQ;` zlfNe$HGi~&mLLX?Hq2tJIX|op(`fhYB2cMDEJlB`8Vks#nMo!hvJFTRPlnC-+e~a@ zvnsJe6-EfwOmCz7#iEd|bX1;3UjnUUq{AXy0eibDh|IfbqlZ{AYhU)1^+b?bOoWO1 z@Vla~(xLK2Mza(*yHXi_(uaK~hvz4_kVra~nT+XA-nT~f?iZKmmfTFcprbVQClACt zv6}z;8Sq9%q z)p`n{H=Pd|+mY>;EBu%aMIb_5etNYKZECw~G;`D`##hNi_Y6P#T$yU@PT4p$b6?{3 ze}rDqgudN*^bqg6F{#DP6?G_hLVA^v$>Omb%b`aDJC4u$8j&0RDj$Uva0S`+#k}`O zQ2mlcQsWr5QYXy@#!)L>&B4(Khfi>V^mQG>mf#+$xnI}l&Bc%$sM*F1qR@mV-87oWX1@D7d z61DQC(1Q|AugeP7DbF)?l@~|1P<;>239#+jUlKb^gBGvk^UZHMPtlj58~#M0jdaFz zVl8G&*rfWFv~;jlrbsS-`tEUQk?!t6d351IX<1oW=GoUFjjuZi8z4w-zsC30W>Uqg+ zW29+O^by1}tkn-sQke})r$IdbPL0W7b770^U_3nOlg8E}Gw6?;>|3oY*;3k0B0<@r zd{YK*9dKVQIYu`2RrOdew{Ep&_73g%1oVF-@sisY^#1^za+X(_F{xDOZO_*XVJIxb_Jb075ZK5*_gSts^U zu|M{~?ZF+NVFCT-R2a9p)2diHf4Zbr{(r;ni|^$mKlf2!&yE0Au>*EiFPoWArL$k3 zE`Ad8WnH9QfBAa*t-qhryWa`@UQTxa&Fv@m?s3bo{(JEDlg7UXbq_QDJ@`N*c59LM z?u{}0*Jbisf8BlZclg+&yMuq8e<8U$_APh8^66CxO}9W%`OyiH9nG(FSg(L;!Ze=nppI#8SG+G5PQBFtw;x*$`-=|HbBF^ z!vp==_JvWe>HUFXI*5Fd_Q{C))p?MnRGXkx{&S}}%=iW$qvDQY`fw=mM$f?RzT9zO z93K7*RwDhn;r)+gUK5p2y^+Trp6U*rZ3USoJbvbhR=RUavG~5rNmalq%-X=x8{?W4 zw9aVVruX2B9Hf(G^+y83FI-9>=7K}KY(F36)&;i9Np|_JdL1yYDCzGqz{d7hcLrE0+^_gTvC)7q-%KsP1Ce|?X`a3I4 z$#;DH+-FX6Mj)PaX*)+pN93EWl8+&3CU7QkKv-TqmZ%9sM^9!ORge&`-^9CVsEq?E zZik&A&Z`;&l~eWn}|>Q$HP_7>sJ;U9@Z_EeQ$WUEO| zel{?*m15v^4&xmWJN*uRju7c+`!Z%1f6+$Dn;KKJKy(Y7&NzI@Cx!uV$6}h`#S-0QsKCVod8<`PvS*kN{3u11l_1OD|p#>$$xuP2X z9lwH(w58Ni@=Q0=gyQe3gH|P-|L9an+If5gZoAiOf>51#P3{2RI_JiyhWsm(=I$q%TuGgt zULh>xG=1;05C;N*8t+z|4n4P!0z3B`4M^|wxHy{B6_6RU?i;W@SM1v^=X5;e-@#tB zKtXcA>3g74E9tzksvVMkw`PscQ=80l(1&N%nLar$M|aP%fpAOEY+uX!9PHZ5ML@0U zad+M?iIo2Y{(o=|@A1t23ls)y|9=3H|MOt&f8NpQyH6Sq02~8=?5C#w!;XRpiCT;5 zCk-Nkb%d8XKUzSzVt&oP8lM0b@!@>!dy_Q9ty*&GQf~3_L2@(l)QV4t60I+Km85b! zv@ZmN>{{`rL=(0B8qWI!DuAH{`V& zl-6z{x0m?D^lsi?{`+=s1~1Rp+|8Had)G?sc1=uF=yq;1sE@5m{#e8dask{Y)tMC@ z=8oXcWaU@uf!U^MuEMoW$wb{@>6_%nMlo zyu>ZyQ0Hqh7xO87Q$GneYLYrXCMY=^Z;<{s5F!qOG;Sq_yC*a8>8KnNNT1N`U^FHlFj=~tvH@D25%;HV!qXQwtvl{!bH zOo2AM>dLh@lo31wJIqJYHk!HxDGZkPRG!*ulx{(@5Ibk^|opd*=MS`X(JR?FsP4*tHfU-Wd7 zSihnkr)3|-0q9Lbvh#T+>n+JUf3A;zf9!kbt2jVs_~zI>Ga^zWRL$ga>O-^5bj;i0 z-A-%z2*)<_qXZJCY45r=I(-S$YS_dXqyb9obI=m z!gf-#@4GjK`G8E}{bE-67pqGDm-<8AGwhb{$?EP!q$8tr8z785*Od^X#aLoiPk#Sj zGPg+wjn9Km6-`|R43^x7De8(g{aIzwn{yuR?1XOINTQ4l0smj|Q3*mQi9zR$rG8V3 zJRQV}1&^vD(Am z5hGwU8&p!nH{8?sjM;tLXPtTEEqQSDr(OeQ>&Naj)ozU5iDgGT?FBZb1Ez2J86jgK z7w7SJIAMCWi!Uy`)1R}Xdo~Dq1|~XeUF~s8Hfs8EnrA-ktDRuvq))n5S#)}$zAS~r zxoBM6;U! zh(TU8fkAp+M@@r&@&yEKO7@%;;O5)~gs)0TD*>69w1bbC^x0w`gLF(A1z4Jw2d>@- z(A|?=yvzP&UbxqM(|5pruo?1a4y*nvn4LAethLg^hO5^omCibFFS+os$S$+*nT5w)AEZdl8p-$oq60^+omn^H^1hs`c9)b*!d__vEZ z1~%V(xlWtRTXiW7dli|DNx@7P?4gp+eAT)RI_-&WPg;}&&LSd$l%l`>T0gYFBa?(+ zGk*jV!mi-bhV*x6QTft;J+0LkQ)9fU@%(K5J0bY?LhpNA8-t-${>_^yQ?&k@kkO8{ zLCNK$s~wt)qvd6F%IZX~ED>pJe;?@N=vTDa*?Pmx#d&jdWQ3>2kLKtz@_sb#DvJ9) zPG|<^+yb&$KMteXM$~XNp6pJ}9dt)3yj27qjobn5y7h9s zIy(X(bmP;kv{v%gaZ#>k)Fj=nbOmClRbzfMPs+G=8eI`KE5)oiZQUV&?}?n|J)3ic zL9PtY9u)MooEV)+54+BQHB_>11`W0FC7a*2T`4pw8>79%r(>FQP)?vl-SlEL2UtNK zt+5O-?a&M~3H8dFty5A)sCls7oFKRCgYC1^uJ%(7Z)Qg#gSi1Yoc_*I{Er-(K(|&$ ziz(S!uh?*O9v|GeUVsb9$LG184j!;%`wWMPv|>jF<)u%}5;q$uZ+ICPK$W4HM#GP4 zw@UDcUAo0*D%jJguczY&Ilrz?W3wZQqx!;TXP@<-7o(S4ufm8#i6HIYn|M6{o8{{# zdiBUN(|GTu|50Nrc3#I*PJ!Fc(+q}c(HJ%w(~EUm#_FCNi7(}7Z%qj4ViHKJ#M$@r zC|)_&<}4KTkuo}&%MYG#Yd;?EXJC-$rD~}g&6x!a)#SuHuXu5*SA*}*#odo4`Bv$x z_ZL*z-`0*?d3W#7(fKRNors5*U$Bg^~SCw z%(N|$e@17w2piT}XEx+|u60FZ8oQKI@;DQ3!4Jn>TB6T(54otcAElZgvun4-%wCTU zPLC@aYOU<08YWie%op)x{+KU-RSaaD%7bc+~$4?vArA zRpoziaM1NOW5q0nUvVD#yzQzA*2cAbz0l^ZjAUSz7qA)cuVwq-|6dX$1|a7?Nh zdVC)0wk)RV3n-Y9uFYqC{;@BqEYHA#ZFqZ5GnMpN#YH~JYP+`CsF@;bxT&Yp>zh;8 z*lKH=tHs{ZZ=ubeZ80Rk2@g8_e`ZS;mXEOlMzK;^}F|dUVDC@c(75C?rIP z)aq(6*|04C*jri4*)Ys0dt;4g)p4@fC_kYhzRf6Yq<{CZb^}|41I}TZZ>TZK*ZZV47MkY26WOxd!{- zyb&o%USG5Pk9q4}*Xc&u&k>~FbYf-sKlCXki0pyNbUjHV*jK9+pH*?&t`iE^&hoif zKRhVUqmXdUqJdm2vF9xmoqndV@U2zveBSK2J0>VF)(`4fJa+5{f?=c2DX7Iu_POQn zdL_PA&Py9m%sBgu(Oon9=f`RwX%7xOQ1AofbJGAWzQf3GP-|tLx#lVeof03Q_oMNG z+Z(+1v;A9ne9)%J*LeG&e>-pn-~YQ`SN-2;^8ZbD?td<0{O!3~Y)Umo30M&2h3XX>KI=F)44Kjm<#4l4HZ#+%FC|3S~A_;Fx%ziDxA z8R+%BQ#M)~Q--L*DGm$Ra;|+FY#sB$jZ@-iC2r=|PtQ zv#=Y709wZXHs$&RQw<1ydpD;yIe41Fg`N z*ejnc+4=^@*3V_@44K^x+(nTzn+i2Q?A=u^J^jYn#`*R@zX)N`lu=T7i*=&--Fz|s z1m=#YSn-Dz*7TWtG!*rekV2vsJEcOxG^nmG5UZ}aAPRnir#~2v)T)Ja6|g&)DH~v# zVue9Hzd&LSuaGn6Cx_mu)uQN{Xt5nyt|fkc!75u8r#z68()dU{cCF=CxNOG&nryda znU`{?coLW$39$Ou;|j6{@M}S6xeI~Gu{aN?I6xok=60v!P`{xqJE$k~vJ|Na>jd=G zxB+}#DdG%Y)B?LtZ4MLoq2n16Rd|#&A;ie!$68iZ_hqMqCaiQIx&08eJz;jRL<+d! zyH0NNEvEP_6-5!f3vk$?y&Hd4kQn+IDJ#2HQCjUv+Mu}_Dc?qfWqC!U`W_5k%Ys4? zQ)s^KvBvl?A@x_6OjD7d2B++-NH>iJcBsQypFv~D6XT@48*c^c&o&<7`2$$_9~Zm| zIy1b%Bcq{N%fC;LzmXzvd3^no>aZ71WvAg=^T{i&MKCqFe3*lwe0B4C!;%Yp)8+yr z#pRkLZ?$BkLE$)Cw20pPBaZvY$Ao4Vx%9Cet5;voBq)qU&=PKJm zV3FtF;@|2X@Q4hoYB+rO>AGs56@EHexHm`)#pXF=1NM%DZv^&PvU?aEb&RH z27$I`$W~1mM->m!Jo+h=6=Rg&I)OsjM#qR?hMpnc=<1uw3HQ*E?SuiD{MVCPl@c#M zQ*WI9WP&?cd$6OI36KFG&d^aQPVe!Cqa4_X3J^K$c~pHDuUImj%1W9q^!ZbF%cIeL znNMStT9M~PMdZMg*84HPuy~VcE?VOZv{loxIXrjUJO-2n{NZ$O8JewW^@1>QA>4n0kTYPywKQncCF-9!G^b6i7~YTTMCC_7I) z%}n|3-H?81YtRy52q`-JF^FW~_gNn%@mfj5V!q`7Lg5jd;p(nD&)`FQv?^7bw0UDhinMujh9sDkGCeVohpK@E z(-fJ}=5X_FfdO4rj>QV1+~5;J>o%H0XC!bF_^`?7Vi+BJeCjVP`9${u+{TlG$=lKH zVYOMd6jM2)|{ME~OC9yuOHxMV*>kRSsb$yT$j#?-?3c)z91 z=PmMMirvHuGwKS&M8q$X^db|Ejg;tl~nh(9**-2pW_i1$r}+k}?|h<|3Y7wY4C!{cvRW z2pv#L{ln};B+JThh_cITWsUR zypu&lG_KFic9m2wPAtL^9x)aH3^4Pu9||1(IQXHE;JBxX$a^9>O`FEGfKA|Wcvf?y zAYpYG$YA3#nUw-o_|ZZqVQNjcztS7oq`i$7ofvs-oBxUKT~?yHHS3jKwpD#FOJ=m3 zAFB*n?hKOefB~k-gMSu9xT;NeRiy9rBcwKEsx_grk>rHPS4>6G zRZ)XO7i7A@2BT6UBKl&JX#t^O9JXSHuP5m{cTtBW?8riDda2Im?3Hx`K2fH&?vXJs zeA!SOU`|dC>^_icak+$3idAnqr4S`#sDhqomh)Tt$gS+*&YtZGkoA%;H(r7Zt=3h# zkrn`J!Nsg)jnqiFBrXV4*c~HMY#AKb*d2Iam>~JZE?;H8* zlhSH`Xn@kTt=#~Z zq`Ei=$rsDtqB!P}tSax)DJarhtj2pRuLIA`dOr_@h0mvS@5`P!4c^6yvZ_7E_KByh zgu4~M^?5(*elPoYS$s0eQ-3M-M;hQUwJM?vioZCj3Ty)eZ1OFU^{c!WQp4;`FZbqf z<@LjP4z6%9vp%hF;{;(d)OkY`a07poh3ZsiW2|EJ> zF}hyJxhhTAIS_~dd`}jR!k&G0q2$29J7V{jJ1j3=Q5=vHwku7jjW@wz7n94BRMLUJ z7L}=~PAh4e2+PgdFMo~wl9K3DLkqO8iR*w0N{4AlmkwXNdo?-TmQzNndf9;5&AFiY zsObBXGOh%~XPLz>NoG!klG(#rhPEFMf2wgheDNj-=?66AHmz{JYgNDimAw!rzM!+l z{Gl_L<|GLi^vc`7mjFFwk~M!_z@9Yp@Z^tGD1*IOnx~}8kB)-%e=rb=fn8`kKf27# zUIbkx`dVR{zE&Gx?ny6nXVg2@`11;3ZIHwcix!0v783sTmPi2$7y)4fk_~EC6yXtV z+N^cerw}_&2qWcuR$xSzkO+5|mvpO#0x3{1b5*(JbG^sS>%aSGET$-;{rxWAyi*Tz zyk+2M?5++=rS3&MII}SKXpH(ClQ`?TykJi|^Ab1g&BlE4Zl1odu*FG!ryIi-N#JH| z&0bipkWs0kN#!5bZ?e-ZcB~JF3jcnMf)W(NE6-#34L987XV^@?zL2&;-*Laj^^_6) zVF4;rG`SG z9u{&8`zgD!Lu>Omv_CWjV4|c&%cNFeH=r7?HOs>aTYRq5&vhN%@{Z)G5u2UK?JSQ_ zLLQx^%yqw4gEzE~*>vDdLr%M`M0&J0J4S1hg$>Lyq0#Ct-$ejO=sCDZqGIy+z{o)Kc@;hbMpI#e?1F`3Bb7@lVun`gf z92&dP4XBuC)(`2yi?T#12%WFLwH9dU_nW~(oK&-GCagSPIar_@BO`91qseVo%D2_Q z-`=xEN8c2o)=|&f-R;K|Qy(GI!v8_Pru+>#K#AEtpsO8~n-0xUn#xq{Z0NXLsp+Ec zUVMC$WT@mednK#xV#2$JR z9NBgF?WP`Mr%(0WtTa44W(ze|JLxNDcXS&o1hZL)LmRzjmTMY@(GN)( zebtTT7wbvCe|uiy< z>(9LNww#%@9r~HQ?+pO+qZ#COTps%Y1Q|_?u0@Bk_KoT=~K<@CX11l!zDJSMG|nVStN*{ZGjR@L2={}vU4gH+A}fTv*#p#Xt{b8 z-D+emVQ)QgE>v%e;jcZS!#0c0E(TACm^u>e^sfD2w?M!7>Ym<`ML9z+B{*p=$)W%% zc()DbhAs{!-!kq9!#)e~%Alp00EYv_ynGeIcUkK)MD07!YRDE{Sb5ZG%|Rnh3+oXh z+Le`nQE_G(f*$yCNlG3O+Bo29{-vIKBmsCaP^tlST94Fvq=x=&8rm!>304aj!#xt- zcR^nimJ4S=h@@KLYIc?!g*3wCuu)wiyw)ZY1@Fxq7ZHh}uLm^rwLO*2ckN1va(66Y z;ew#IR+4)K+5koKM^<@0br`}xGXh|&H1S^TRdryGifpeAd+DO3E{mm4rIL%w+As9a z6~9RQ4OE{Ozek_@rd@6&3ejQPeTAjT|4a$wrF32NzfTwlRIEMP8bAW;yll{A$ZDY7 z8V&wR_vw$Hl|E;Iz3W|sm!UG>0>Nv$7hIZffCz06@Gw)Ne5QSkt7b0N`%-Snc0G{z z53ce!k6Ed{+U(vxBt8*j^x<7R2V(0#%q#EBzTS;D?#1BL{;&B$KL5zF-tk>M9>MtU z`WAnG2&WR;2DejHM*t;0KnTtDIrE^?C>PyfZ^Ss~dVb4{F>+>o3{)mw#{2r$wKG2Q z8rUd9vb)CiJ=eF3WBs|MbJ=Pv3#-^Q93ctUh3vYJ_76&nb3jol;dFH{_x9$F?sM;5 zR+7wCen<2CN>==evhb!A6H3C!n%D#IjJsmXqEO^)VH+)-=v(J$klZ)7yja6 z_=K9-x0k_u9#)sNz3XxZ zHqUtTC(bbFkkN|ahxC=v-E?B`-$v)iq`%mS3jj0HEVs&})ad&eiY^#8%^LcsH35|>+f*eSzpaOKkM z>XpFTY=>xH?OD1yI^HYM(H_JJ8_GyP92`1vbw)6d~LIcJ!()F%*xrk~3_P76 zW%#T5_Y9pHB?`QkZgg^ILi>!WeC`af=SDk|j%E~%me*$W1+yJii}i5-!v=q_R~58x z&G_Lj#7D@aEiaz)UM){-waD~AbqC_O5h>?1awb4c6ek}wurli4h(vx4{Jh9_eqgt9?rTlzK2-Sh-I98(6 zl?o-L`N)EjM$R@@EBR@bZ4~|!i&=sMo8EFbPYbZcf}%3BDO?_dH%SC8Dx;E#g&h## z#M|5W2l6)V3rTEP<8UZzPeS}P@Tfp29NLHyAI@$maGW{Q0KjF0Ag)I+NiEkftvq|I zU^vVwf5EQZbQ*}i@mY^vN|C1TQYjMkFT$C3MD#v9rvy|9+*vo*?v? z>`d6Osg};TN=XoH`wZ&bCb#FgUo|GAW2jm1?lqjl_SN-WyJzQH!GfKgQ@hpF7k~-A z|MX1cV25u*)O|)#9;o?kUf|Sn?xX@f8EWnIfjCjH?U~)DErQ^Xligz*kiIE5&=;&) z)}@b4Wa`t--!)U*6`5AdES6^rt@6!1Rd4r@jy?AmFR+Uz8T@)MiVXdO6x?Dr4shGSJ5C0e z7N3j-#H|gOv;p5bJUX<^dMz>uJWS$Ulgb}jWrE-igZhX#Z>3R0mA>#utlPzw1F_Bt z$*+@l+LP%9Jcl6&)q#Y_U$*!}#3U#&wX<_HIvJi#x9<-4P$tOAr;@*IBFZK0tE}?- zyKpunBLv9cv(XiZZSWFnbF}JthsX$^HZlg12s=|e4DYPqVyw%NUL%^s>g zXhWRT9iuD*(l!rH#%q%r{{l!=adA$PYqr?2?MC_I$3NgfQBI}&IzU@y&0mXV#lOUW zv_6+QRSqNuFB#Zngn6ncZS`4#RB;cO(%3s`=c^as!8mJHMYJf&s9!V3nRVC z+9xCK;SgkWf?`~$Qa8`CgM&IP;OzeD*Cv$R1=ynq#nyF@`HSB9QG zqIji+2ZD$A>TLcRF1=4Sf7N@(9$1uD6?I?HtF~=>MVP4Mha{;_>P_K%b(vm!V`@^* zj*_tc(Vej!t4!UOW>RFQb23wbU8+n(2jB!L8S69wu99BLpji%|KAj$}tN+x?TjN?F zaF{!iL6Wn)Nm7>Ei9_ZFF06TBmFT(y1~6$z?za}LxrJ4Xo1%2GMg!Xqn}M#Ndu?yD^9eO!exG9g$qwP{X9SE?1qA@;~_b+c5WXvo|^C6n6j)1sqliEL}lwdC8B z%9e1)QT>bVuQ{M@jOihIO_tD_S zj(8gc0}PFLh(zmFl&N^;Fu9{eAHgc{I@XB=M+b}$EiKyG^^ig-mNrm}yh0o+RWgXy zXzxmL#NHMAM4Ye6*}|bDbu@+0mi}^5)PCq0$n+SNeO5x!M#b47jfAFwM-{pywl!2u z^rkIAM+X999t=Go<=SwG>UrJ0lA(r*7HfjtG|{hnZR?eq8$Zkn47EH%;tG%UCp;Lt z`e;kz_VJo9MsiI)cEPztM|ds+iz3FSW(ie6Nw_)jfi~4uS=QHj+>fKyaC2lg9NHbT zw>Z5#WW0(FR-6ghE5qn-w#4%WZ*0s<@L+-mwPXDZRLxQxdZXYcu^~aETWeSKM90$R zqU}lDQ8aTx$NmM<5aB-ycw1{qhXrsW$|fp1MVD)bkpXAgYZ2oamnkQv58WUpNBi30 z8`H-#W;Fwj+wBg^wPZ@?Bmid!&(ZYS_Mxu7C&t|d<{9L{qj}=EJuIz5LK4fEnb)%8 zHB3DxR4e3ql(1vuUw4#c`d5PWEczda^;x(y~ zfJ1szXTu+G4R<|o`-RBmH{tC83iw;4GY{6C5%aTTZ3{LIqkQUq)q2_I=LfM9t@ByU zlEyK-smeq|${uQK$%a%s8te6ck|i0sz;sNSct^wO*T#)=d0C|Zzx#+%5uNtZj_^$Z z4aBj6PBnam!}OOT79X;VOm#*1`D@!{QG9T#p<1?&_VcehY^ctRXNxO!M2S;WWR!#= zTB;|&aOlxB~>slXQ<%&zD1!dXO|oVj2IzBU7`W9WRWnjz%CN3aL7#>7f-vPZ(GgFbJ(za8=_S&>HSl?UL9(l8Qo5w zz}>PlnBmZbqobPnFcUb#%@uTigSK5fs;5D|==8N|rWAiuoS z_3TY{*~4_Vr-8vkPhah4&5!)DV=T3=$&HWXz?r8-C%9zexwU;g=ae4Bo%nw69shcJ9{j%GusuU^eJtmpV)MwMILe`bz_ z{YMSs$bEtCrP_nPX5SFJwaU#^d7#INTOuYWmu#X3@z`eZ{ZuS&IGy`VzULBNP+o+! zTcr!b4vZcrXqoz-F5kOmE#DgP>LyhK#T=1esAg}u5z41qXTLA2Q<^P!dHRf~AN)vg z447D8O|`EPnI*h$vbq`j zBP`HD^IT|S$>5I;YS^m0B@<-T@`PeJku#>4t?$%rxbnF&f+Xp)FK_vPgvC&$bn5P; zo<9>r%WkOHGCbd!s#?byb^sNyvbLAYP5e0i^H*7sh)P?T6@&d#7r;bIwx2Uf6`gO7 zPSbBs{e4;~FuvWR(;Z&#IZ?Qa$UtpSiKN>)l zowOBCB@|8;kjNXpMwqT;8uX1m3&(eC#Ak}Tih7ZfV*k2Bkwxw^kCU?E2(`Doh)e)C z@!=Es)r9fH%3bDDKJC59n2KZu94r3Jv_2Ef&0Y~w`NY=VHjIx#u4b4u*E*NvU{JRS ze*Zj!iRZd)@h*gU{iB<*NB*&wbP*;gj|XXTA)w_p_%7U7e4NglcR5LvFa!D>rE+E# zAc#o3rdZr8A34jw)U}NK?032rlx$kDam(7|S{K`=eP1PfXShO+gjrwVd{sl0P2x9Zkp3Y;XB#C<3 z9yMV#vd<9piJ&AWm&m7%thjHVEl^}SIke0EdX zK22vG0PMtoUM0iJzEdq5HXocb(R7ZBDi4(PQiaC8xUHIUadcv+!$N6fI-8=>*W?74 zNTSW5gMLV{!qK1ZA-S80ZTYX#O07R%_{3D~$R!jkXLi0hnMZ9$(Qmi&pt%UG))Q@l zYqG*jwe?Nw3y!|$V)C+o$SsRB4nwF1GzbitZ=6g596@O9xd~ai;Q}hLYF_H9iV<`- zsCT6OusP&7i2b@2MM)%HNQt{lKml8FWxB+pgwJ^|7z*E*s+SM()V;9w@Aj+ASA*4a zRQPwdn~!|iGpsGL(+uVNgPjU2DG!J+UzLHnsd zkCoxOEZEn0e8$zu4KBk_A-(nNR;MhjWdh|vmTojdPa_*_T@{ov%V@uDYWK6D?FE|w z+~eY96$`@qK_GNdFVCszEZ{V#|LvpZ%fxiOiMeSnrc^|O(4a|tu6z%yOp`)y+rQMp za!va{XIzzMFg%MC0*37S_>8F2c_c3ON>0a)h(!Co;|gMzboSg@x6^tvGx?Hw`lC2x zfLs77eL2F7vF_P73^}6vD)d#tVfHnh%l&V^Xc#b`zI(c7OJ3aC zCvoWpc%f5Kd6fF}Wz|y2{kG1J`D!Q6@?SLzW|q%u^GP8MT~Yfgp;bAd{d87vzT^Bu z(%))=wajO3A`;IqQ(nlBY7{0RL-b9vdSM6DX(8gU?DOIAXH;22y6e~j5>mYh_nLr0 zSzwTg`S@-MnPO!zwM={5J}_FNg0jyP8w~KONr9;(f%bV~=z4`->zzK-LdT&G z3J4RDeA*Hhl}ps6ae73}sK*<`gG|7Dz;<9YX_Xqm zUf~YqOP(xl?y8eo)6{+cdn zHqF!tqD35Ixx-_HT)aBX*urS>D|Dnjew=ZGR>C)+S0GdmE{2FVw;XRJ)rIsa*Zv8G zs7q&y{vPPrR2lGr?I+Fkgtmk8Enj*I@u{hCaL}={scJ+imC7=VrY+A`C>FC3RD+jV zFpO%u8`i$6!SSs z2$36ch|Fkpl3VJ|5P$Q=bx1r$Fmx&48EbN3e4r6WZxKv&){Hv)OSln>xq#5efUXPE zCKsLZa&=fl08YtS$u4}sRlL5G+ZDjaNCy7#Mo@Y9-*3LYSbi{*gU{kb4Noa5)=trA zz&r8^5%!IN%c>0!#YIjJ?T_T$u83e3JNjHx8*C*MRS|L0qsb69Pen@*_xK5*MwDKA}%J?(k_QQXkd@wM6N=~VSJ zU+NZSqd}-lSF-iKU|arqZ937qp)pW770wU&0Aht?n$IEt^1D){zXO(L<$X8%Ra?Gj zTA20cqHQtxZHAh$!1tEMT@RDgscDM+<>h9cFFFgO$4on~PNz=eYK}^|H%I;1xP)k24yaf7wXbvH5$3z_2K#t{3P01lq zoXG}7aiSczAbOW|&OKkwU0?XH7K^o5@B2Rg|Ls6lGLZNr5*cVD`y@x-XBqmML$)5#lCqqAD03boXI(R;Toc=Sbt{FnKu-HDx` z$vGp6`dGaRp?G7bm*R@#&7Jg3&sOYkKbrquvs~^MvIBvH3*L3E5K&cVN9is9)Ta}V z42!!LKO3bq^cGE$s{-Zb)E#AgN9|e4l{F=OUyp94AQa_gzhbrzt#yZHIlFclFNK;= z7VF(0vhMhKIIDj}z_%%g9CPC&*Vz|BDw zJXz=TqpCL8P&Kf7eM#s&56nO7!>{#qF4}u#3iI!Ps@3$uUM#!IIXHB7oHr1Qjl&Bj zXc5D;-ouul_cx>HY?-PnWrtKd`xr}`vEVS=s~0Z>QFiWUTvRF!cz8)KJPiia&j(Cm zRQ!~;ZdhS8gfQuJO|2sb{}@|VWw(~u8ikTPntW^p;ydX4KtJ-YsvP#F!V9ofEc213 z9d9;WgSVVFt6FO+$c2!_m3wWCbFi;S++|8cE<(e;Y2Odh9MZzp7Tiy zWw)c@gLodv8Dvb3b2Y1XcwTntge@vg)M)~rW#EI7iuSxG!4V04ZPr)VY1;2r#H{VQN4B|7& zI(#?6o@7~;f*XP*jthW%en#_r+Uc;g24F$=WCBpbhsSDbi=0hLyb(X^iFX-dy1cUi z`%^ZUAKG1-2gYeFhYJfBVFQKLOdKmcvnvy)f(TREn`YC?G}$67$QD{M7Lo_Ait$m1t>24S7gFG9b6Qr zsDk@BvI_FhX3EzMbr$*Sx3W~OgP{7sD7pI;7wY`%}=|NgALZ>ehVj_$j*5QAw` zA;CMGz06?m8%QN^h&a~E{C&RYQ&-&1AXWCi1fRZ%Msk4_F|#6fVDHeP|HfL1w6yAc zdE?k@!i_g^1nsnm2Sdi!*Jg0*>NyT~Q}89#_e%(+3kj@^FtX|_iA~z zA&f5Jn@<=1-I%*_4qeb*6p$WsIt0-P*rO)P7+qnHbDr<8Z-l}A2MJ#mE;BbW&TIg4 zY`aMD)xmHwp!l-gmteif60!=}ydU}*DiN*wZ235P%W;5e##qmp@Gtqk9vk@N^15T5 z-R8%duj&_|g>_0{kE}==cY7G3h1JWaYz0?3(z8iitNG{yuxa+1LFr1pLJH*Z!n*UD zxRG2|ktgEGIq@MloB2EU;{n6W4x#)PqK)<~2vtCxPlnNO9|c}wKxZ%mxXb1#qGAw! zq+5_>C6(ef4uoFLD0@9DhmAaPuKn7XVedP^Z&Fg-5Ix|m+GjrD7zK6)nv=Be-MyM~ z(`rTMkORG6yQ(mMt{1;(-zIHYWx+*dyH32%%ne^ zX$%IegeVF#JYoFql+ONw)fR(`#Y(8AekE%z8ajiaUh&GzB9H?c>y#dZbgNU;_di(H z6?46RQZQgL2jVlvo%+TczYc?<`V27_TQi}ot_BTCczOP7v*sAT#rcBo)!xvsn{J_t zIXvdSLZ!lUYFoYh-poU!V>=7w zWF%dgN?`Qu0A5=T|41$JuVjDCZIXIUmD2Qu1KCSE3ND9M9PQEZl^Jz-ZylbKih7GI z|M-n7DTiWQkfXv~XHu(PEqrJl{SUFUsZC5`9f#rNr04)3;|-Lz({RB6tW1%yL#Cmo5U{Md6D# z_wy6Z;e&*3_$LLNYd8-i2I{3K{rwV*t(_QW@{X)5mOM1{<#_JDDbs>`8&dyk4`mrB z;GevjC?&fmy8(x-%m)~3qp&=>%=nM{g5?KuUh9zaxQ)>`f~6!FtrXen1iEpZJq~N3 zmrQCcMQcT1)_r4esvMQAlj1*dXP*vu&mQGiKjWiM+;oV(;@(L>pO{bEAJ|ykYVK^d z$V$*n0Ml>KS=G;zP_(Pnl}J{;B0~Ki-bs0GaL|+ka^rL;drT3Y%d4 zKJ&EzPB~^$rrXRh`0^@V!(!FO%D>}8HX z+i`cLVr%#pHhu$6vthNBbUg*w!GMH0er%L1?d9nA39G+z*0mdHQqj{vYF`OA$7y*X zdat3);lO!Q=|Iy4k6YpRY9-1qq_auIxAXb1yyI4(CVa5V_#mcENgqISm2SH7Pfd7 z<7F%{P|pA?)f3pI-RcJ@x`Docie&;GPl>V{1+vQrF^y!Xc0mQXh z#nhYt3g2^&t8QGsdipHi^E&wpyQa?CZ z7&+(f_TdfR2NxYBoP!%pqHZcG7-T29u?lGlYv^;d?RgBjeeCgyTYhVEKv-JV1YI0 zA$|uTn{8;Tvg6~L=zKV;lx|Y?SgkG17x`H`qv@_TKgB&WsYRoi=>&J5yH}-VQ(N*L za6XGe?$6`GgZdgA9Zi0WatS(6pj|8=bB|gFJhA|kPwAUj5xffu}(0|m*yh)qE`@y4;o&C zTcrPJxbyA2=PK|bqME`O_#0s7&rsUE?uOY>($2geOp1&96lM=4s;lWJA2T=e%ZDGT z3vXu51^E?n6eb*pfU6VK&Jk-hefoMc9RN8@FFxfrv7*Q6hr)DJ_$`tFf3+_Tl4#?^ zV-#-?C=zpF@yo`|#o7Hz>q~v~o(pl3Rq1G?u* zRRXRWEg)9pQMLhCGO)h-6!^t@LTH>R9gcOdX6-Adtu_yjf<-#vPwN)lH@H3GyK_Hs zmOcNWYq`*`(k~2{PI(f)BfaVymBx$gEp}C}uN6Yce;0>RhVv5coX))~`_b;;jfp(tAVx4sTq8%Q#otHnomvj%1!*j;B_=gko^ z0DFx)1stjpkRMF0oCHdbBe;86>j!CNCwn{k8M%2mWp+P4s5|dc^wd0=%7=4}z7@l5 z%`XN5U&pRzB#w4x79@Z@Ne!Bl$yu-XJ(myVV_!4X*s%U@isC+BlUkN(gA`A_$4|&b z-rJb$ID}_|=opIU4!>NpD30Og{a@HffN{Fyn{q1f@+W?bw>_O)FRc-G1i&sv6%PGO z1AkhpiHGeDW<7bfw&aRd{LdplG!kY19ct?y`40Uo{(N)d(Es0~ZGLBe-b>qW_S3N| Qy93155@u0#$vf_U0kh=ZH~;_u literal 0 HcmV?d00001 From 62a8b09282f3e4195b6ca1674c7e2e7d671fe228 Mon Sep 17 00:00:00 2001 From: Mike Swantek Date: Wed, 28 May 2025 09:44:41 -0400 Subject: [PATCH 19/44] revise code spaces steps --- docs/github_code_spaces_steps.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/docs/github_code_spaces_steps.md b/docs/github_code_spaces_steps.md index a835757..6379085 100644 --- a/docs/github_code_spaces_steps.md +++ b/docs/github_code_spaces_steps.md @@ -32,11 +32,7 @@ You can run this solution using GitHub Codespaces. The button will open a web-ba ![Image showing the password prompt for azure](../img/provisioning/enterpassword.png) - **Prompting for MFA** - - ![Image showing the pop up window in the web browser for azd auth](../img/provisioning/azdauthpopup.png) - -7. Repeat the same process in #6 using the “az login” command. The [Azure CLI](https://learn.microsoft.com/en-us/cli/azure/what-is-azure-cli?view=azure-cli-latest) is used to validate available AI model quota. +7. Return to the codespaces window and repeat the same process in #6 using the “az login” command. The [Azure CLI](https://learn.microsoft.com/en-us/cli/azure/what-is-azure-cli?view=azure-cli-latest) is used to validate available AI model quota. ![image showing theaz login in the vs code terminal](../img/provisioning/az_login.png) 8. Return to the codespaces window now. In the terminal window, begin by initializing the environment by typing the command “azd init” From cdc583d8c0736cebdcfaa20fe865e5166bde0239 Mon Sep 17 00:00:00 2001 From: Mike Swantek Date: Wed, 28 May 2025 09:50:04 -0400 Subject: [PATCH 20/44] Additional pre-provisioning model quota check documentation --- docs/github_code_spaces_steps.md | 8 +++++++- img/provisioning/preprovision_output.png | Bin 0 -> 53069 bytes 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 img/provisioning/preprovision_output.png diff --git a/docs/github_code_spaces_steps.md b/docs/github_code_spaces_steps.md index 6379085..4b0bf40 100644 --- a/docs/github_code_spaces_steps.md +++ b/docs/github_code_spaces_steps.md @@ -52,12 +52,18 @@ You can run this solution using GitHub Codespaces. The button will open a web-ba **Be sure to remember the vm password. This will be used in a later step. You are still required to log into Azure once you connect through the virtual machine. -11. The automated model quota check will run, and will check if the location selected will have the necessary quota for the AI Models that are listed in the parameters file prior to deploying any resources. If the location selected has sufficient quota for the models you plan to deploy, the provisioning will begin without notification. +11. The automated model quota check will run, and will check if the location selected will have the necessary quota for the AI Models that are listed in the parameters file prior to deploying any resources. + ![image showing model quota pre-provision code executing](../img/provisioning/preprovision_output.png) + + + If the location selected has sufficient quota for the models you plan to deploy, the provisioning will begin without notification. ![image showing model quota pre-provision pass](../img/provisioning/preprovision_success.png) If the location selected does not have the available quota for the models selected in your parameters, there will be a message back to the user, prior to any provisioning of resources. This will allow the developer to change the location of the provisiong and try again. Note that in our example, Italy North had capacity for gpt-4o but not for text-embedding-ada-002. This terminated the entire provisioning, because both models could not be deployed due to a quota issue. + ![image showing model quota pre-provision fail](../img/provisioning/preprovision_fail.png) + 12. After completeing the required paramters that you were prompted for, and a successful model quota validation, the provisioning of resources will run and deploy the Network Isolated AI Foundry development portal and dependent resources in about 20 minutes. diff --git a/img/provisioning/preprovision_output.png b/img/provisioning/preprovision_output.png new file mode 100644 index 0000000000000000000000000000000000000000..a9ba97c9c3547be8e35aeb7bf76e08d07f5c4b16 GIT binary patch literal 53069 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 b|1503jWJ8E?ppl<{wF0S|E=Pyq5uB^TCu?C literal 0 HcmV?d00001 From 6b331c802bb0137fa710148f908d4464293c6dc6 Mon Sep 17 00:00:00 2001 From: Mike Swantek Date: Wed, 28 May 2025 09:52:45 -0400 Subject: [PATCH 21/44] additional documentation for model quota validation during azd up --- docs/github_code_spaces_steps.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/github_code_spaces_steps.md b/docs/github_code_spaces_steps.md index 4b0bf40..c781d40 100644 --- a/docs/github_code_spaces_steps.md +++ b/docs/github_code_spaces_steps.md @@ -32,7 +32,7 @@ You can run this solution using GitHub Codespaces. The button will open a web-ba ![Image showing the password prompt for azure](../img/provisioning/enterpassword.png) -7. Return to the codespaces window and repeat the same process in #6 using the “az login” command. The [Azure CLI](https://learn.microsoft.com/en-us/cli/azure/what-is-azure-cli?view=azure-cli-latest) is used to validate available AI model quota. +7. Return to the codespaces window and type “az login”. The [Azure CLI](https://learn.microsoft.com/en-us/cli/azure/what-is-azure-cli?view=azure-cli-latest) is used to validate available AI model quota. ![image showing theaz login in the vs code terminal](../img/provisioning/az_login.png) 8. Return to the codespaces window now. In the terminal window, begin by initializing the environment by typing the command “azd init” From cea86372d14eb2292a8f3d5cc794fadd95e52efe Mon Sep 17 00:00:00 2001 From: Mike Swantek Date: Wed, 28 May 2025 09:53:58 -0400 Subject: [PATCH 22/44] additional model quota preprovision check documentation --- docs/github_code_spaces_steps.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/github_code_spaces_steps.md b/docs/github_code_spaces_steps.md index c781d40..154bd44 100644 --- a/docs/github_code_spaces_steps.md +++ b/docs/github_code_spaces_steps.md @@ -49,7 +49,7 @@ You can run this solution using GitHub Codespaces. The button will open a web-ba This step will allow you to choose from the subscriptions you have available, based on the account you logged in with in the login step. Next it will prompt you for the region to deploy the resources into as well as any additional Azure resources to be provisioned and configured. - **Be sure to remember the vm password. This will be used in a later step. You are still required to log into Azure once you connect through the virtual machine. + **Be sure to remember the vm password. This will be used in a later step. You are still required to log into Azure once you connect through the virtual machine. 11. The automated model quota check will run, and will check if the location selected will have the necessary quota for the AI Models that are listed in the parameters file prior to deploying any resources. From eeed30c0c93fe7687212ce57c27f7ae3dc8f9896 Mon Sep 17 00:00:00 2001 From: Rohini-Microsoft Date: Wed, 4 Jun 2025 11:00:27 +0530 Subject: [PATCH 23/44] update the image path --- docs/post_deployment_steps.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/post_deployment_steps.md b/docs/post_deployment_steps.md index b681eae..2a7b5ec 100644 --- a/docs/post_deployment_steps.md +++ b/docs/post_deployment_steps.md @@ -4,22 +4,22 @@ Follow these steps to check the creation of the required private endpoints in th One way to check if the access is private to the hub is to launch the AI Foundry hub from the portal. -![Image showing if network isolation is checked](img/provisioning/checkNetworkIsolation3.png) +![Image showing if network isolation is checked](../img/provisioning/checkNetworkIsolation3.png) When a user that is not connected through the virtual network via an RDP approved connection will see the following screen in their browser. This is the intended behavior! -![Image showing the virtual machine in the browser](img/provisioning/checkNetworkIsolation4.png) +![Image showing the virtual machine in the browser](../img/provisioning/checkNetworkIsolation4.png) A more thorough check is to look for the networking settings and checking for private endpoints. 1. Go to the Azure Portal and select your Azure AI hub that was just created. 2. Click on Settings and then Networking. - ![Image showing the Azure Portal for AI Foundry Hub and the settings blade](img/provisioning/checkNetworkIsolation1.png) + ![Image showing the Azure Portal for AI Foundry Hub and the settings blade](../img/provisioning/checkNetworkIsolation1.png) 3. Open the Workspace managed outbound access tab. - ![Image showing the Azure Portal for AI Foundry Hub and the Workspace managed outbound access tab](img/provisioning/checkNetworkIsolation2.png) + ![Image showing the Azure Portal for AI Foundry Hub and the Workspace managed outbound access tab](../img/provisioning/checkNetworkIsolation2.png) Here, you will find the private endpoints that are connected to the resources within the hub managed virtual network. Ensure that these private endpoints are active. The hub should show that Public access is ‘disabled’. @@ -27,31 +27,31 @@ A more thorough check is to look for the networking settings and checking for pr ## Connecting to the isolated network via RDP 1. Navigate to the resource group where the isolated AI Foundry was deployed to and select the virtual machine. - ![Image showing the Azure Portal for the virtual machine](img/provisioning/checkNetworkIsolation5.png) + ![Image showing the Azure Portal for the virtual machine](../img/provisioning/checkNetworkIsolation5.png) 2. Be sure that the Virtual Machine is running. If not, start the VM. - ![Image showing the Azure Portal VM and the start/stop button](img/provisioning/checkNetworkIsolation6.png) + ![Image showing the Azure Portal VM and the start/stop button](../img/provisioning/checkNetworkIsolation6.png) 3. Select “Bastion” under the ‘Connect’ heading in the VM resource. - ![Image showing the bastion blade selected](img/provisioning/checkNetworkIsolation7.png) + ![Image showing the bastion blade selected](../img/provisioning/checkNetworkIsolation7.png) 4. Supply the username and the password you created as environment variables and press the connect button. - ![Image showing the screen to enter the VM Admin info and the connect to bastion button](img/provisioning/checkNetworkIsolation8.png) + ![Image showing the screen to enter the VM Admin info and the connect to bastion button](../img/provisioning/checkNetworkIsolation8.png) 5. Your virtual machine will launch and you will see a different screen. - ![Image showing the opening of the Virtual machine in another browser tab](img/provisioning/checkNetworkIsolation9.png) + ![Image showing the opening of the Virtual machine in another browser tab](../img/provisioning/checkNetworkIsolation9.png) 6. Launch Edge browser and navigate to your AI Foundry Hub. https://ai.azure.com Sign in using your credentials. 7. You are challenged by MFA to connect. - ![Image showing the Multi Factor Authentication popup](img/provisioning/checkNetworkIsolation10.png) + ![Image showing the Multi Factor Authentication popup](../img/provisioning/checkNetworkIsolation10.png) 8. You will now be able to view the Foundry Hub which is contained in an isolated network. - ![Image showing the Azure Foundry AI Hub with a private bubble icon](img/provisioning/checkNetworkIsolation11.png) + ![Image showing the Azure Foundry AI Hub with a private bubble icon](../img/provisioning/checkNetworkIsolation11.png) From 152211e736f10696740b2f4803653436e7f2e8fb Mon Sep 17 00:00:00 2001 From: Mike Swantek Date: Tue, 17 Jun 2025 11:16:26 -0400 Subject: [PATCH 24/44] replace the in place module with the avm pattern module path --- infra/modules/cognitive-services/service.bicep | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/infra/modules/cognitive-services/service.bicep b/infra/modules/cognitive-services/service.bicep index 4c1e3f7..b798c80 100644 --- a/infra/modules/cognitive-services/service.bicep +++ b/infra/modules/cognitive-services/service.bicep @@ -83,7 +83,7 @@ var privateDnsZones = [ var nameFormatted = take(toLower(name), 24) -module cognitiveService '../avm/cognitive-services/main.bicep' = { +module cognitiveService 'br/public:avm/res/cognitive-services/account:0.11.0' = { name: take('cog-${kind}-${name}-deployment', 64) params: { name: nameFormatted From 5ecb399c5db4e020dd3086a31a62f32dcead4b57 Mon Sep 17 00:00:00 2001 From: Prajwal D C Date: Wed, 18 Jun 2025 18:40:33 +0530 Subject: [PATCH 25/44] fix: Deployment Issue with AZD version 1.17 fixed --- infra/main.bicep | 22 ++++++------ infra/main.json | 92 +++++++++++++++++++++++++++--------------------- 2 files changed, 62 insertions(+), 52 deletions(-) diff --git a/infra/main.bicep b/infra/main.bicep index 499b4ac..cb29508 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -15,7 +15,7 @@ param connections connectionType[] = [] param aiModelDeployments deploymentsType[] = [] @description('Specifies whether creating an Azure Container Registry.') -param acrEnabled bool +param acrEnabled bool = false @description('Specifies the size of the jump-box Virtual Machine.') param vmSize string = 'Standard_DS4_v2' @@ -41,7 +41,7 @@ param userObjectId string = deployer().objectId param allowedIpAddress string = '' @description('Specifies if Microsoft APIM is deployed.') -param apiManagementEnabled bool +param apiManagementEnabled bool = false @description('Specifies the publisher email for the API Management service. Defaults to admin@[name].com.') param apiManagementPublisherEmail string = 'admin@${name}.com' @@ -50,37 +50,37 @@ param apiManagementPublisherEmail string = 'admin@${name}.com' param networkIsolation bool = true @description('Whether to include Cosmos DB in the deployment.') -param cosmosDbEnabled bool +param cosmosDbEnabled bool = false @description('Optional. List of Cosmos DB databases to deploy.') param cosmosDatabases sqlDatabaseType[] = [] @description('Whether to include SQL Server in the deployment.') -param sqlServerEnabled bool +param sqlServerEnabled bool = false @description('Optional. List of SQL Server databases to deploy.') param sqlServerDatabases databasePropertyType[] = [] @description('Whether to include Azure AI Search in the deployment.') -param searchEnabled bool +param searchEnabled bool = false @description('Whether to include Azure AI Content Safety in the deployment.') -param contentSafetyEnabled bool +param contentSafetyEnabled bool = false @description('Whether to include Azure AI Vision in the deployment.') -param visionEnabled bool +param visionEnabled bool = false @description('Whether to include Azure AI Language in the deployment.') -param languageEnabled bool +param languageEnabled bool = false @description('Whether to include Azure AI Speech in the deployment.') -param speechEnabled bool +param speechEnabled bool = false @description('Whether to include Azure AI Translator in the deployment.') -param translatorEnabled bool +param translatorEnabled bool = false @description('Whether to include Azure Document Intelligence in the deployment.') -param documentIntelligenceEnabled bool +param documentIntelligenceEnabled bool = false var defaultTags = { 'azd-env-name': name diff --git a/infra/main.json b/infra/main.json index 71326a9..08a69d6 100644 --- a/infra/main.json +++ b/infra/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "1285677619681818261" + "version": "0.36.1.42791", + "templateHash": "16413515920777914038" } }, "definitions": { @@ -1634,7 +1634,6 @@ }, "location": { "type": "string", - "defaultValue": "[resourceGroup().location]", "metadata": { "description": "Specifies the location for all the Azure resources. Defaults to the location of the resource group." } @@ -1661,6 +1660,7 @@ }, "acrEnabled": { "type": "bool", + "defaultValue": false, "metadata": { "description": "Specifies whether creating an Azure Container Registry." } @@ -1712,6 +1712,7 @@ }, "apiManagementEnabled": { "type": "bool", + "defaultValue": false, "metadata": { "description": "Specifies if Microsoft APIM is deployed." } @@ -1732,6 +1733,7 @@ }, "cosmosDbEnabled": { "type": "bool", + "defaultValue": false, "metadata": { "description": "Whether to include Cosmos DB in the deployment." } @@ -1748,6 +1750,7 @@ }, "sqlServerEnabled": { "type": "bool", + "defaultValue": false, "metadata": { "description": "Whether to include SQL Server in the deployment." } @@ -1764,42 +1767,49 @@ }, "searchEnabled": { "type": "bool", + "defaultValue": false, "metadata": { "description": "Whether to include Azure AI Search in the deployment." } }, "contentSafetyEnabled": { "type": "bool", + "defaultValue": false, "metadata": { "description": "Whether to include Azure AI Content Safety in the deployment." } }, "visionEnabled": { "type": "bool", + "defaultValue": false, "metadata": { "description": "Whether to include Azure AI Vision in the deployment." } }, "languageEnabled": { "type": "bool", + "defaultValue": false, "metadata": { "description": "Whether to include Azure AI Language in the deployment." } }, "speechEnabled": { "type": "bool", + "defaultValue": false, "metadata": { "description": "Whether to include Azure AI Speech in the deployment." } }, "translatorEnabled": { "type": "bool", + "defaultValue": false, "metadata": { "description": "Whether to include Azure AI Translator in the deployment." } }, "documentIntelligenceEnabled": { "type": "bool", + "defaultValue": false, "metadata": { "description": "Whether to include Azure Document Intelligence in the deployment." } @@ -5641,8 +5651,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "10563293765969438544" + "version": "0.36.1.42791", + "templateHash": "14424734402412352330" } }, "parameters": { @@ -6321,8 +6331,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "9442281453257963936" + "version": "0.36.1.42791", + "templateHash": "11746621349310683559" } }, "parameters": { @@ -12706,8 +12716,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "12630130910180117756" + "version": "0.36.1.42791", + "templateHash": "2158520837294746606" } }, "parameters": { @@ -18934,8 +18944,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "9749329569698294872" + "version": "0.36.1.42791", + "templateHash": "11482270866070363596" } }, "definitions": { @@ -30959,8 +30969,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "12389686054107465462" + "version": "0.36.1.42791", + "templateHash": "4400142003788830420" } }, "definitions": { @@ -38060,8 +38070,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "13783926077882220732" + "version": "0.36.1.42791", + "templateHash": "16919614201922288466" } }, "definitions": { @@ -40774,8 +40784,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "13783926077882220732" + "version": "0.36.1.42791", + "templateHash": "16919614201922288466" } }, "definitions": { @@ -43490,8 +43500,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "13783926077882220732" + "version": "0.36.1.42791", + "templateHash": "16919614201922288466" } }, "definitions": { @@ -46206,8 +46216,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "13783926077882220732" + "version": "0.36.1.42791", + "templateHash": "16919614201922288466" } }, "definitions": { @@ -48919,8 +48929,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "13783926077882220732" + "version": "0.36.1.42791", + "templateHash": "16919614201922288466" } }, "definitions": { @@ -51635,8 +51645,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "13783926077882220732" + "version": "0.36.1.42791", + "templateHash": "16919614201922288466" } }, "definitions": { @@ -54348,8 +54358,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "13783926077882220732" + "version": "0.36.1.42791", + "templateHash": "16919614201922288466" } }, "definitions": { @@ -57091,8 +57101,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "18300401960964991877" + "version": "0.36.1.42791", + "templateHash": "2345622020738803913" } }, "definitions": { @@ -62509,8 +62519,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "10286244752482293080" + "version": "0.36.1.42791", + "templateHash": "14156198524686936179" } }, "parameters": { @@ -63083,8 +63093,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "15582758383558794550" + "version": "0.36.1.42791", + "templateHash": "12408064070292908432" } }, "definitions": { @@ -74099,8 +74109,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "5435873910134176815" + "version": "0.36.1.42791", + "templateHash": "1542994590355543426" } }, "definitions": { @@ -78207,8 +78217,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "13278853949319290770" + "version": "0.36.1.42791", + "templateHash": "9207373860269569676" } }, "parameters": { @@ -86140,8 +86150,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "12134709961749323497" + "version": "0.36.1.42791", + "templateHash": "244112826431292448" } }, "definitions": { @@ -93339,8 +93349,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "5330058909966791926" + "version": "0.36.1.42791", + "templateHash": "18362011243916437863" } }, "definitions": { From e35e15bb0ba9d3aa4d3f663b036cef000412690a Mon Sep 17 00:00:00 2001 From: Prajwal D C Date: Wed, 18 Jun 2025 21:02:55 +0530 Subject: [PATCH 26/44] feat: Updated the main.json --- infra/main.json | 18506 +++++----------------------------------------- 1 file changed, 1900 insertions(+), 16606 deletions(-) diff --git a/infra/main.json b/infra/main.json index 08a69d6..44770cf 100644 --- a/infra/main.json +++ b/infra/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.36.1.42791", - "templateHash": "16413515920777914038" + "templateHash": "15881347792888664493" } }, "definitions": { @@ -1813,6 +1813,23 @@ "metadata": { "description": "Whether to include Azure Document Intelligence in the deployment." } + }, + "networkAcls": { + "type": "object", + "defaultValue": { + "defaultAction": "Deny", + "bypass": "AzureServices" + }, + "metadata": { + "description": "Optional. A collection of rules governing the accessibility from specific network locations." + } + }, + "projectName": { + "type": "string", + "defaultValue": "[format('{0}proj', parameters('name'))]", + "metadata": { + "description": "Name of the first project" + } } }, "variables": { @@ -18916,7 +18933,7 @@ }, "mode": "Incremental", "parameters": { - "name": { + "storageName": { "value": "[format('st{0}{1}', parameters('name'), variables('resourceToken'))]" }, "location": { @@ -18931,7 +18948,7 @@ "value": "[reference('logAnalyticsWorkspace').outputs.resourceId.value]" }, "roleAssignments": { - "value": "[concat(if(empty(parameters('userObjectId')), createArray(), createArray(createObject('principalId', parameters('userObjectId'), 'principalType', 'User', 'roleDefinitionIdOrName', 'Storage Blob Data Contributor'))), createArray(createObject('principalId', reference('cognitiveServices').outputs.aiServicesSystemAssignedMIPrincipalId.value, 'principalType', 'ServicePrincipal', 'roleDefinitionIdOrName', 'Storage Blob Data Contributor')), if(parameters('searchEnabled'), createArray(createObject('principalId', reference('aiSearch').outputs.systemAssignedMIPrincipalId.value, 'principalType', 'ServicePrincipal', 'roleDefinitionIdOrName', 'Storage Blob Data Contributor')), createArray()))]" + "value": "[concat(if(empty(parameters('userObjectId')), createArray(), createArray(createObject('principalId', parameters('userObjectId'), 'principalType', 'User', 'roleDefinitionIdOrName', 'Storage Blob Data Contributor'))), createArray(createObject('principalId', reference('cognitiveServices').outputs.aiServicesSystemAssignedMIPrincipalId.value, 'principalType', 'ServicePrincipal', 'roleDefinitionIdOrName', 'Storage Blob Data Contributor')), if(parameters('searchEnabled'), createArray(createObject('principalId', if(parameters('searchEnabled'), reference('aiSearch').outputs.systemAssignedMIPrincipalId.value, ''), 'principalType', 'ServicePrincipal', 'roleDefinitionIdOrName', 'Storage Blob Data Contributor')), createArray()))]" }, "tags": { "value": "[variables('allTags')]" @@ -18945,7 +18962,7 @@ "_generator": { "name": "bicep", "version": "0.36.1.42791", - "templateHash": "11482270866070363596" + "templateHash": "8737464205872383016" } }, "definitions": { @@ -19026,7 +19043,7 @@ } }, "parameters": { - "name": { + "storageName": { "type": "string", "metadata": { "description": "Name of the Storage Account." @@ -19082,7 +19099,7 @@ } }, "variables": { - "nameFormatted": "[take(toLower(parameters('name')), 24)]" + "nameFormatted": "[take(toLower(parameters('storageName')), 24)]" }, "resources": { "blobPrivateDnsZone": { @@ -30889,11 +30906,11 @@ } }, "outputs": { - "name": { + "storageName": { "type": "string", "value": "[reference('storageAccount').outputs.name.value]" }, - "resourceId": { + "storageResourceId": { "type": "string", "value": "[reference('storageAccount').outputs.resourceId.value]" } @@ -30929,6 +30946,9 @@ "networkIsolation": { "value": "[parameters('networkIsolation')]" }, + "networkAcls": { + "value": "[parameters('networkAcls')]" + }, "virtualNetworkResourceId": "[if(parameters('networkIsolation'), createObject('value', reference('network').outputs.virtualNetworkId.value), createObject('value', ''))]", "virtualNetworkSubnetResourceId": "[if(parameters('networkIsolation'), createObject('value', reference('network').outputs.vmSubnetId.value), createObject('value', ''))]", "logAnalyticsWorkspaceResourceId": { @@ -30970,7 +30990,7 @@ "_generator": { "name": "bicep", "version": "0.36.1.42791", - "templateHash": "4400142003788830420" + "templateHash": "13701972717161400247" } }, "definitions": { @@ -31839,7 +31859,7 @@ "minLength": 3, "maxLength": 12, "metadata": { - "description": "The name of the environment/application. Use alphanumeric characters only." + "description": "Name of the Cognitive Services resource. Must be unique in the resource group." } }, "resourceToken": { @@ -31936,6 +31956,12 @@ "metadata": { "description": "Whether to include Azure Document Intelligence in the deployment." } + }, + "networkAcls": { + "type": "object", + "metadata": { + "description": "Optional. A collection of rules governing the accessibility from specific network locations." + } } }, "resources": { @@ -38050,6 +38076,9 @@ "networkIsolation": { "value": "[parameters('networkIsolation')]" }, + "networkAcls": { + "value": "[parameters('networkAcls')]" + }, "virtualNetworkSubnetResourceId": "[if(parameters('networkIsolation'), createObject('value', parameters('virtualNetworkSubnetResourceId')), createObject('value', ''))]", "privateDnsZonesResourceIds": "[if(parameters('networkIsolation'), createObject('value', createArray(reference('cognitiveServicesPrivateDnsZone').outputs.resourceId.value, reference('openAiPrivateDnsZone').outputs.resourceId.value)), createObject('value', createArray()))]", "logAnalyticsWorkspaceResourceId": { @@ -38058,7 +38087,7 @@ "aiModelDeployments": { "value": "[parameters('aiModelDeployments')]" }, - "roleAssignments": "[if(empty(parameters('userObjectId')), createObject('value', createArray()), createObject('value', createArray(createObject('principalId', parameters('userObjectId'), 'principalType', 'User', 'roleDefinitionIdOrName', 'Cognitive Services OpenAI Contributor'))))]", + "roleAssignments": "[if(empty(parameters('userObjectId')), createObject('value', createArray()), createObject('value', createArray(createObject('principalId', parameters('userObjectId'), 'principalType', 'User', 'roleDefinitionIdOrName', 'Cognitive Services OpenAI Contributor'), createObject('principalId', parameters('userObjectId'), 'principalType', 'User', 'roleDefinitionIdOrName', 'Cognitive Services Contributor'), createObject('principalId', parameters('userObjectId'), 'principalType', 'User', 'roleDefinitionIdOrName', 'Cognitive Services User'))))]", "tags": { "value": "[parameters('tags')]" } @@ -38071,7 +38100,7 @@ "_generator": { "name": "bicep", "version": "0.36.1.42791", - "templateHash": "16919614201922288466" + "templateHash": "2348080591288311162" } }, "definitions": { @@ -38376,6 +38405,12 @@ "metadata": { "description": "Optional. Tags to be applied to the resources." } + }, + "networkAcls": { + "type": "object", + "metadata": { + "description": "Optional. A collection of rules governing the accessibility from specific network locations." + } } }, "variables": { @@ -38416,6 +38451,9 @@ "kind": { "value": "[parameters('kind')]" }, + "allowProjectManagement": { + "value": true + }, "managedIdentities": { "value": { "systemAssigned": true @@ -38441,6 +38479,9 @@ "roleAssignments": { "value": "[parameters('roleAssignments')]" }, + "networkAcls": { + "value": "[parameters('networkAcls')]" + }, "privateEndpoints": "[if(parameters('networkIsolation'), createObject('value', createArray(createObject('privateDnsZoneGroup', createObject('privateDnsZoneGroupConfigs', variables('privateDnsZones')), 'subnetResourceId', parameters('virtualNetworkSubnetResourceId')))), createObject('value', createArray()))]" }, "template": { @@ -38450,8 +38491,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "616090829858030869" + "version": "0.36.1.42791", + "templateHash": "16135659971302525380" }, "name": "Cognitive Services", "description": "This module deploys a Cognitive Service." @@ -38517,112 +38558,110 @@ } }, "metadata": { - "__bicep_export!": true + "__bicep_export!": true, + "description": "The type for the private endpoint output." } }, - "deploymentsType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Specify the name of cognitive service account deployment." - } - }, - "model": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of Cognitive Services account deployment model." - } - }, - "format": { - "type": "string", - "metadata": { - "description": "Required. The format of Cognitive Services account deployment model." - } - }, - "version": { - "type": "string", - "metadata": { - "description": "Required. The version of Cognitive Services account deployment model." - } + "deploymentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of cognitive service account deployment." + } + }, + "model": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of Cognitive Services account deployment model." } }, - "metadata": { - "description": "Required. Properties of Cognitive Services account deployment model." - } - }, - "sku": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the resource model definition representing SKU." - } - }, - "capacity": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The capacity of the resource model definition representing SKU." - } - }, - "tier": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The tier of the resource model definition representing SKU." - } - }, - "size": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The size of the resource model definition representing SKU." - } - }, - "family": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The family of the resource model definition representing SKU." - } + "format": { + "type": "string", + "metadata": { + "description": "Required. The format of Cognitive Services account deployment model." } }, - "nullable": true, - "metadata": { - "description": "Optional. The resource model definition representing SKU." + "version": { + "type": "string", + "metadata": { + "description": "Required. The version of Cognitive Services account deployment model." + } } }, - "raiPolicyName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of RAI policy." + "metadata": { + "description": "Required. Properties of Cognitive Services account deployment model." + } + }, + "sku": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource model definition representing SKU." + } + }, + "capacity": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The capacity of the resource model definition representing SKU." + } + }, + "tier": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The tier of the resource model definition representing SKU." + } + }, + "size": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The size of the resource model definition representing SKU." + } + }, + "family": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The family of the resource model definition representing SKU." + } } }, - "versionUpgradeOption": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The version upgrade option." - } + "nullable": true, + "metadata": { + "description": "Optional. The resource model definition representing SKU." + } + }, + "raiPolicyName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of RAI policy." + } + }, + "versionUpgradeOption": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The version upgrade option." } } }, - "nullable": true, "metadata": { - "__bicep_export!": true + "__bicep_export!": true, + "description": "The type for a cognitive services account deployment." } }, - "endpointsType": { + "endpointType": { "type": "object", "properties": { "name": { @@ -38640,9 +38679,9 @@ } } }, - "nullable": true, "metadata": { - "__bicep_export!": true + "__bicep_export!": true, + "description": "The type for a cognitive services account endpoint." } }, "secretsExportConfigurationType": { @@ -38670,7 +38709,8 @@ } }, "metadata": { - "__bicep_export!": true + "__bicep_export!": true, + "description": "The type of the secrets exported to the provided Key Vault." } }, "_1.privateEndpointCustomDnsConfigType": { @@ -38695,7 +38735,7 @@ }, "metadata": { "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -38737,7 +38777,7 @@ }, "metadata": { "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -38778,7 +38818,7 @@ }, "metadata": { "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -38807,7 +38847,7 @@ "metadata": { "description": "An AVM-aligned type for the output of the secret set via the secrets export feature.", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -38844,7 +38884,7 @@ "metadata": { "description": "An AVM-aligned type for a customer-managed key. To be used if the resource type does not support auto-rotation of the customer-managed key.", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -38966,7 +39006,7 @@ "metadata": { "description": "An AVM-aligned type for a diagnostic setting. To be used if both logs & metrics are supported by the resource provider.", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -38996,7 +39036,7 @@ "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.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -39024,7 +39064,7 @@ "metadata": { "description": "An AVM-aligned type for a managed identity configuration. To be used if both a system-assigned & user-assigned identities are supported by the resource provider.", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -39065,6 +39105,13 @@ "description": "Required. Resource ID of the subnet where the endpoint needs to be created." } }, + "resourceGroupResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the Resource Group the Private Endpoint will be created in. If not specified, the Resource Group of the provided Virtual Network Subnet is used." + } + }, "privateDnsZoneGroup": { "$ref": "#/definitions/_1.privateEndpointPrivateDnsZoneGroupType", "nullable": true, @@ -39154,19 +39201,12 @@ "metadata": { "description": "Optional. Enable/Disable usage telemetry for module." } - }, - "resourceGroupName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Specify if you want to deploy the Private Endpoint into a different Resource Group than the main resource." - } } }, "metadata": { "description": "An AVM-aligned type for a private endpoint. To be used if the private endpoint's default service / groupId can be assumed (i.e., for services that only have one Private Endpoint type like 'vault' for key vault).", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -39241,7 +39281,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.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -39257,7 +39297,7 @@ "metadata": { "description": "A map of the exported secrets", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } } @@ -39481,7 +39521,11 @@ } }, "deployments": { - "$ref": "#/definitions/deploymentsType", + "type": "array", + "items": { + "$ref": "#/definitions/deploymentType" + }, + "nullable": true, "metadata": { "description": "Optional. Array of deployments about cognitive service accounts to create." } @@ -39492,6 +39536,13 @@ "metadata": { "description": "Optional. Key vault reference and secret settings for the module's secrets export." } + }, + "allowProjectManagement": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable/Disable project management feature for AI Foundry." + } } }, "variables": { @@ -39530,6 +39581,7 @@ "Cognitive Services Speech Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0e75ca1e-0464-4b4d-8b93-68208a576181')]", "Cognitive Services Speech User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f2dc8367-1007-4938-bd23-fe263f013447')]", "Cognitive Services User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a97b65f3-24c7-4388-baec-2e87135dc908')]", + "Azure AI Developer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '64702f94-c441-49e6-a78b-ef80e0188fee')]", "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", @@ -39542,7 +39594,7 @@ "condition": "[and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), not(empty(tryGet(parameters('customerManagedKey'), 'keyName')))))]", "existing": true, "type": "Microsoft.KeyVault/vaults/keys", - "apiVersion": "2023-02-01", + "apiVersion": "2023-07-01", "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[2]]", "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[4]]", "name": "[format('{0}/{1}', last(split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')), tryGet(parameters('customerManagedKey'), 'keyName'))]" @@ -39551,7 +39603,7 @@ "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2024-03-01", - "name": "[format('46d3xbcp.res.cognitiveservices-account.{0}.{1}', replace('0.10.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "name": "[format('46d3xbcp.res.cognitiveservices-account.{0}.{1}', replace('0.11.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -39571,7 +39623,7 @@ "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId')))]", "existing": true, "type": "Microsoft.KeyVault/vaults", - "apiVersion": "2023-02-01", + "apiVersion": "2023-07-01", "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[2]]", "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[4]]", "name": "[last(split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/'))]" @@ -39580,14 +39632,14 @@ "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId')))]", "existing": true, "type": "Microsoft.ManagedIdentity/userAssignedIdentities", - "apiVersion": "2023-01-31", + "apiVersion": "2025-01-31-preview", "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/')[2]]", "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/')[4]]", "name": "[last(split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/'))]" }, "cognitiveService": { "type": "Microsoft.CognitiveServices/accounts", - "apiVersion": "2023-05-01", + "apiVersion": "2025-04-01-preview", "name": "[parameters('name')]", "kind": "[parameters('kind')]", "identity": "[variables('identity')]", @@ -39597,6 +39649,7 @@ "name": "[parameters('sku')]" }, "properties": { + "allowProjectManagement": "[parameters('allowProjectManagement')]", "customSubDomainName": "[parameters('customSubDomainName')]", "networkAcls": "[if(not(empty(coalesce(parameters('networkAcls'), createObject()))), createObject('defaultAction', tryGet(parameters('networkAcls'), 'defaultAction'), 'virtualNetworkRules', coalesce(tryGet(parameters('networkAcls'), 'virtualNetworkRules'), createArray()), 'ipRules', coalesce(tryGet(parameters('networkAcls'), 'ipRules'), createArray())), null())]", "publicNetworkAccess": "[if(not(equals(parameters('publicNetworkAccess'), null())), parameters('publicNetworkAccess'), if(not(empty(parameters('networkAcls'))), 'Enabled', 'Disabled'))]", @@ -39611,8 +39664,8 @@ "dynamicThrottlingEnabled": "[parameters('dynamicThrottlingEnabled')]" }, "dependsOn": [ - "cMKKeyVault::cMKKey", "cMKKeyVault", + "cMKKeyVault::cMKKey", "cMKUserAssignedIdentity" ] }, @@ -39624,7 +39677,7 @@ "batchSize": 1 }, "type": "Microsoft.CognitiveServices/accounts/deployments", - "apiVersion": "2023-05-01", + "apiVersion": "2025-04-01-preview", "name": "[format('{0}/{1}', parameters('name'), coalesce(tryGet(coalesce(parameters('deployments'), createArray())[copyIndex()], 'name'), format('{0}-deployments', parameters('name'))))]", "properties": { "model": "[coalesce(parameters('deployments'), createArray())[copyIndex()].model]", @@ -39775,8 +39828,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "15954548978129725136" + "version": "0.34.44.8038", + "templateHash": "12389807800450456797" }, "name": "Private Endpoints", "description": "This module deploys a Private Endpoint." @@ -40185,7 +40238,7 @@ "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2024-03-01", - "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.10.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.11.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -40203,7 +40256,7 @@ }, "privateEndpoint": { "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2023-11-01", + "apiVersion": "2024-05-01", "name": "[parameters('name')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", @@ -40291,8 +40344,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "5440815542537978381" + "version": "0.34.44.8038", + "templateHash": "13997305779829540948" }, "name": "Private Endpoint Private DNS Zone Groups", "description": "This module deploys a Private Endpoint Private DNS Zone Group." @@ -40364,12 +40417,12 @@ "privateEndpoint": { "existing": true, "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2023-11-01", + "apiVersion": "2024-05-01", "name": "[parameters('privateEndpointName')]" }, "privateDnsZoneGroup": { "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2023-11-01", + "apiVersion": "2024-05-01", "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", "properties": { "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" @@ -40433,7 +40486,7 @@ "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('privateEndpoint', '2023-11-01', 'full').location]" + "value": "[reference('privateEndpoint', '2024-05-01', 'full').location]" }, "customDnsConfigs": { "type": "array", @@ -40487,7 +40540,7 @@ "value": "[last(split(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '/'))]" }, "secretsToSet": { - "value": "[union(createArray(), if(contains(parameters('secretsExportConfiguration'), 'accessKey1Name'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'accessKey1Name'), 'value', listKeys(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), '2023-05-01').key1)), createArray()), if(contains(parameters('secretsExportConfiguration'), 'accessKey2Name'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'accessKey2Name'), 'value', listKeys(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), '2023-05-01').key2)), createArray()))]" + "value": "[union(createArray(), if(contains(parameters('secretsExportConfiguration'), 'accessKey1Name'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'accessKey1Name'), 'value', listKeys('cognitiveService', '2025-04-01-preview').key1)), createArray()), if(contains(parameters('secretsExportConfiguration'), 'accessKey2Name'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'accessKey2Name'), 'value', listKeys('cognitiveService', '2025-04-01-preview').key2)), createArray()))]" } }, "template": { @@ -40497,8 +40550,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "1340925512665498953" + "version": "0.36.1.42791", + "templateHash": "1200612323329026557" } }, "definitions": { @@ -40527,7 +40580,7 @@ "metadata": { "description": "An AVM-aligned type for the output of the secret set via the secrets export feature.", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -40550,7 +40603,7 @@ "metadata": { "description": "An AVM-aligned type for the secret to set via the secrets export feature.", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } } @@ -40576,7 +40629,7 @@ "keyVault": { "existing": true, "type": "Microsoft.KeyVault/vaults", - "apiVersion": "2022-07-01", + "apiVersion": "2023-07-01", "name": "[parameters('keyVaultName')]" }, "secrets": { @@ -40648,7 +40701,7 @@ "value": "[reference('cognitiveService').endpoint]" }, "endpoints": { - "$ref": "#/definitions/endpointsType", + "$ref": "#/definitions/endpointType", "metadata": { "description": "All endpoints available for the cognitive services account, types depends on the cognitive service kind." }, @@ -40660,14 +40713,14 @@ "metadata": { "description": "The principal ID of the system assigned identity." }, - "value": "[tryGet(tryGet(reference('cognitiveService', '2023-05-01', 'full'), 'identity'), 'principalId')]" + "value": "[tryGet(tryGet(reference('cognitiveService', '2025-04-01-preview', 'full'), 'identity'), 'principalId')]" }, "location": { "type": "string", "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('cognitiveService', '2023-05-01', 'full').location]" + "value": "[reference('cognitiveService', '2025-04-01-preview', 'full').location]" }, "exportedSecrets": { "$ref": "#/definitions/secretsOutputType", @@ -40768,6 +40821,9 @@ "networkIsolation": { "value": "[parameters('networkIsolation')]" }, + "networkAcls": { + "value": "[parameters('networkAcls')]" + }, "virtualNetworkSubnetResourceId": "[if(parameters('networkIsolation'), createObject('value', parameters('virtualNetworkSubnetResourceId')), createObject('value', ''))]", "privateDnsZonesResourceIds": "[if(parameters('networkIsolation'), createObject('value', createArray(reference('cognitiveServicesPrivateDnsZone').outputs.resourceId.value)), createObject('value', createArray()))]", "logAnalyticsWorkspaceResourceId": { @@ -40785,7 +40841,7 @@ "_generator": { "name": "bicep", "version": "0.36.1.42791", - "templateHash": "16919614201922288466" + "templateHash": "2348080591288311162" } }, "definitions": { @@ -41090,6 +41146,12 @@ "metadata": { "description": "Optional. Tags to be applied to the resources." } + }, + "networkAcls": { + "type": "object", + "metadata": { + "description": "Optional. A collection of rules governing the accessibility from specific network locations." + } } }, "variables": { @@ -41130,6 +41192,9 @@ "kind": { "value": "[parameters('kind')]" }, + "allowProjectManagement": { + "value": true + }, "managedIdentities": { "value": { "systemAssigned": true @@ -41155,6 +41220,9 @@ "roleAssignments": { "value": "[parameters('roleAssignments')]" }, + "networkAcls": { + "value": "[parameters('networkAcls')]" + }, "privateEndpoints": "[if(parameters('networkIsolation'), createObject('value', createArray(createObject('privateDnsZoneGroup', createObject('privateDnsZoneGroupConfigs', variables('privateDnsZones')), 'subnetResourceId', parameters('virtualNetworkSubnetResourceId')))), createObject('value', createArray()))]" }, "template": { @@ -41164,8 +41232,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "616090829858030869" + "version": "0.36.1.42791", + "templateHash": "16135659971302525380" }, "name": "Cognitive Services", "description": "This module deploys a Cognitive Service." @@ -41231,112 +41299,110 @@ } }, "metadata": { - "__bicep_export!": true + "__bicep_export!": true, + "description": "The type for the private endpoint output." } }, - "deploymentsType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Specify the name of cognitive service account deployment." - } - }, - "model": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of Cognitive Services account deployment model." - } - }, - "format": { - "type": "string", - "metadata": { - "description": "Required. The format of Cognitive Services account deployment model." - } - }, - "version": { - "type": "string", - "metadata": { - "description": "Required. The version of Cognitive Services account deployment model." - } + "deploymentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of cognitive service account deployment." + } + }, + "model": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of Cognitive Services account deployment model." } }, - "metadata": { - "description": "Required. Properties of Cognitive Services account deployment model." - } - }, - "sku": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the resource model definition representing SKU." - } - }, - "capacity": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The capacity of the resource model definition representing SKU." - } - }, - "tier": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The tier of the resource model definition representing SKU." - } - }, - "size": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The size of the resource model definition representing SKU." - } - }, - "family": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The family of the resource model definition representing SKU." - } + "format": { + "type": "string", + "metadata": { + "description": "Required. The format of Cognitive Services account deployment model." } }, - "nullable": true, - "metadata": { - "description": "Optional. The resource model definition representing SKU." + "version": { + "type": "string", + "metadata": { + "description": "Required. The version of Cognitive Services account deployment model." + } } }, - "raiPolicyName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of RAI policy." + "metadata": { + "description": "Required. Properties of Cognitive Services account deployment model." + } + }, + "sku": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource model definition representing SKU." + } + }, + "capacity": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The capacity of the resource model definition representing SKU." + } + }, + "tier": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The tier of the resource model definition representing SKU." + } + }, + "size": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The size of the resource model definition representing SKU." + } + }, + "family": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The family of the resource model definition representing SKU." + } } }, - "versionUpgradeOption": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The version upgrade option." - } + "nullable": true, + "metadata": { + "description": "Optional. The resource model definition representing SKU." + } + }, + "raiPolicyName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of RAI policy." + } + }, + "versionUpgradeOption": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The version upgrade option." } } }, - "nullable": true, "metadata": { - "__bicep_export!": true + "__bicep_export!": true, + "description": "The type for a cognitive services account deployment." } }, - "endpointsType": { + "endpointType": { "type": "object", "properties": { "name": { @@ -41354,9 +41420,9 @@ } } }, - "nullable": true, "metadata": { - "__bicep_export!": true + "__bicep_export!": true, + "description": "The type for a cognitive services account endpoint." } }, "secretsExportConfigurationType": { @@ -41384,7 +41450,8 @@ } }, "metadata": { - "__bicep_export!": true + "__bicep_export!": true, + "description": "The type of the secrets exported to the provided Key Vault." } }, "_1.privateEndpointCustomDnsConfigType": { @@ -41409,7 +41476,7 @@ }, "metadata": { "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -41451,7 +41518,7 @@ }, "metadata": { "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -41492,7 +41559,7 @@ }, "metadata": { "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -41521,7 +41588,7 @@ "metadata": { "description": "An AVM-aligned type for the output of the secret set via the secrets export feature.", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -41558,7 +41625,7 @@ "metadata": { "description": "An AVM-aligned type for a customer-managed key. To be used if the resource type does not support auto-rotation of the customer-managed key.", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -41680,7 +41747,7 @@ "metadata": { "description": "An AVM-aligned type for a diagnostic setting. To be used if both logs & metrics are supported by the resource provider.", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -41710,7 +41777,7 @@ "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.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -41738,7 +41805,7 @@ "metadata": { "description": "An AVM-aligned type for a managed identity configuration. To be used if both a system-assigned & user-assigned identities are supported by the resource provider.", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -41779,6 +41846,13 @@ "description": "Required. Resource ID of the subnet where the endpoint needs to be created." } }, + "resourceGroupResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the Resource Group the Private Endpoint will be created in. If not specified, the Resource Group of the provided Virtual Network Subnet is used." + } + }, "privateDnsZoneGroup": { "$ref": "#/definitions/_1.privateEndpointPrivateDnsZoneGroupType", "nullable": true, @@ -41868,19 +41942,12 @@ "metadata": { "description": "Optional. Enable/Disable usage telemetry for module." } - }, - "resourceGroupName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Specify if you want to deploy the Private Endpoint into a different Resource Group than the main resource." - } } }, "metadata": { "description": "An AVM-aligned type for a private endpoint. To be used if the private endpoint's default service / groupId can be assumed (i.e., for services that only have one Private Endpoint type like 'vault' for key vault).", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -41955,7 +42022,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.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -41971,7 +42038,7 @@ "metadata": { "description": "A map of the exported secrets", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } } @@ -42195,7 +42262,11 @@ } }, "deployments": { - "$ref": "#/definitions/deploymentsType", + "type": "array", + "items": { + "$ref": "#/definitions/deploymentType" + }, + "nullable": true, "metadata": { "description": "Optional. Array of deployments about cognitive service accounts to create." } @@ -42206,6 +42277,13 @@ "metadata": { "description": "Optional. Key vault reference and secret settings for the module's secrets export." } + }, + "allowProjectManagement": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable/Disable project management feature for AI Foundry." + } } }, "variables": { @@ -42244,6 +42322,7 @@ "Cognitive Services Speech Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0e75ca1e-0464-4b4d-8b93-68208a576181')]", "Cognitive Services Speech User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f2dc8367-1007-4938-bd23-fe263f013447')]", "Cognitive Services User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a97b65f3-24c7-4388-baec-2e87135dc908')]", + "Azure AI Developer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '64702f94-c441-49e6-a78b-ef80e0188fee')]", "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", @@ -42256,7 +42335,7 @@ "condition": "[and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), not(empty(tryGet(parameters('customerManagedKey'), 'keyName')))))]", "existing": true, "type": "Microsoft.KeyVault/vaults/keys", - "apiVersion": "2023-02-01", + "apiVersion": "2023-07-01", "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[2]]", "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[4]]", "name": "[format('{0}/{1}', last(split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')), tryGet(parameters('customerManagedKey'), 'keyName'))]" @@ -42265,7 +42344,7 @@ "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2024-03-01", - "name": "[format('46d3xbcp.res.cognitiveservices-account.{0}.{1}', replace('0.10.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "name": "[format('46d3xbcp.res.cognitiveservices-account.{0}.{1}', replace('0.11.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -42285,7 +42364,7 @@ "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId')))]", "existing": true, "type": "Microsoft.KeyVault/vaults", - "apiVersion": "2023-02-01", + "apiVersion": "2023-07-01", "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[2]]", "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[4]]", "name": "[last(split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/'))]" @@ -42294,14 +42373,14 @@ "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId')))]", "existing": true, "type": "Microsoft.ManagedIdentity/userAssignedIdentities", - "apiVersion": "2023-01-31", + "apiVersion": "2025-01-31-preview", "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/')[2]]", "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/')[4]]", "name": "[last(split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/'))]" }, "cognitiveService": { "type": "Microsoft.CognitiveServices/accounts", - "apiVersion": "2023-05-01", + "apiVersion": "2025-04-01-preview", "name": "[parameters('name')]", "kind": "[parameters('kind')]", "identity": "[variables('identity')]", @@ -42311,6 +42390,7 @@ "name": "[parameters('sku')]" }, "properties": { + "allowProjectManagement": "[parameters('allowProjectManagement')]", "customSubDomainName": "[parameters('customSubDomainName')]", "networkAcls": "[if(not(empty(coalesce(parameters('networkAcls'), createObject()))), createObject('defaultAction', tryGet(parameters('networkAcls'), 'defaultAction'), 'virtualNetworkRules', coalesce(tryGet(parameters('networkAcls'), 'virtualNetworkRules'), createArray()), 'ipRules', coalesce(tryGet(parameters('networkAcls'), 'ipRules'), createArray())), null())]", "publicNetworkAccess": "[if(not(equals(parameters('publicNetworkAccess'), null())), parameters('publicNetworkAccess'), if(not(empty(parameters('networkAcls'))), 'Enabled', 'Disabled'))]", @@ -42325,8 +42405,8 @@ "dynamicThrottlingEnabled": "[parameters('dynamicThrottlingEnabled')]" }, "dependsOn": [ - "cMKKeyVault::cMKKey", "cMKKeyVault", + "cMKKeyVault::cMKKey", "cMKUserAssignedIdentity" ] }, @@ -42338,7 +42418,7 @@ "batchSize": 1 }, "type": "Microsoft.CognitiveServices/accounts/deployments", - "apiVersion": "2023-05-01", + "apiVersion": "2025-04-01-preview", "name": "[format('{0}/{1}', parameters('name'), coalesce(tryGet(coalesce(parameters('deployments'), createArray())[copyIndex()], 'name'), format('{0}-deployments', parameters('name'))))]", "properties": { "model": "[coalesce(parameters('deployments'), createArray())[copyIndex()].model]", @@ -42489,8 +42569,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "15954548978129725136" + "version": "0.34.44.8038", + "templateHash": "12389807800450456797" }, "name": "Private Endpoints", "description": "This module deploys a Private Endpoint." @@ -42899,7 +42979,7 @@ "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2024-03-01", - "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.10.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.11.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -42917,7 +42997,7 @@ }, "privateEndpoint": { "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2023-11-01", + "apiVersion": "2024-05-01", "name": "[parameters('name')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", @@ -43005,8 +43085,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "5440815542537978381" + "version": "0.34.44.8038", + "templateHash": "13997305779829540948" }, "name": "Private Endpoint Private DNS Zone Groups", "description": "This module deploys a Private Endpoint Private DNS Zone Group." @@ -43078,12 +43158,12 @@ "privateEndpoint": { "existing": true, "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2023-11-01", + "apiVersion": "2024-05-01", "name": "[parameters('privateEndpointName')]" }, "privateDnsZoneGroup": { "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2023-11-01", + "apiVersion": "2024-05-01", "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", "properties": { "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" @@ -43147,7 +43227,7 @@ "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('privateEndpoint', '2023-11-01', 'full').location]" + "value": "[reference('privateEndpoint', '2024-05-01', 'full').location]" }, "customDnsConfigs": { "type": "array", @@ -43201,7 +43281,7 @@ "value": "[last(split(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '/'))]" }, "secretsToSet": { - "value": "[union(createArray(), if(contains(parameters('secretsExportConfiguration'), 'accessKey1Name'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'accessKey1Name'), 'value', listKeys(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), '2023-05-01').key1)), createArray()), if(contains(parameters('secretsExportConfiguration'), 'accessKey2Name'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'accessKey2Name'), 'value', listKeys(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), '2023-05-01').key2)), createArray()))]" + "value": "[union(createArray(), if(contains(parameters('secretsExportConfiguration'), 'accessKey1Name'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'accessKey1Name'), 'value', listKeys('cognitiveService', '2025-04-01-preview').key1)), createArray()), if(contains(parameters('secretsExportConfiguration'), 'accessKey2Name'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'accessKey2Name'), 'value', listKeys('cognitiveService', '2025-04-01-preview').key2)), createArray()))]" } }, "template": { @@ -43211,8 +43291,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "1340925512665498953" + "version": "0.36.1.42791", + "templateHash": "1200612323329026557" } }, "definitions": { @@ -43241,7 +43321,7 @@ "metadata": { "description": "An AVM-aligned type for the output of the secret set via the secrets export feature.", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -43264,7 +43344,7 @@ "metadata": { "description": "An AVM-aligned type for the secret to set via the secrets export feature.", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } } @@ -43290,7 +43370,7 @@ "keyVault": { "existing": true, "type": "Microsoft.KeyVault/vaults", - "apiVersion": "2022-07-01", + "apiVersion": "2023-07-01", "name": "[parameters('keyVaultName')]" }, "secrets": { @@ -43362,7 +43442,7 @@ "value": "[reference('cognitiveService').endpoint]" }, "endpoints": { - "$ref": "#/definitions/endpointsType", + "$ref": "#/definitions/endpointType", "metadata": { "description": "All endpoints available for the cognitive services account, types depends on the cognitive service kind." }, @@ -43374,14 +43454,14 @@ "metadata": { "description": "The principal ID of the system assigned identity." }, - "value": "[tryGet(tryGet(reference('cognitiveService', '2023-05-01', 'full'), 'identity'), 'principalId')]" + "value": "[tryGet(tryGet(reference('cognitiveService', '2025-04-01-preview', 'full'), 'identity'), 'principalId')]" }, "location": { "type": "string", "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('cognitiveService', '2023-05-01', 'full').location]" + "value": "[reference('cognitiveService', '2025-04-01-preview', 'full').location]" }, "exportedSecrets": { "$ref": "#/definitions/secretsOutputType", @@ -43484,6 +43564,9 @@ "networkIsolation": { "value": "[parameters('networkIsolation')]" }, + "networkAcls": { + "value": "[parameters('networkAcls')]" + }, "virtualNetworkSubnetResourceId": "[if(parameters('networkIsolation'), createObject('value', parameters('virtualNetworkSubnetResourceId')), createObject('value', ''))]", "privateDnsZonesResourceIds": "[if(parameters('networkIsolation'), createObject('value', createArray(reference('cognitiveServicesPrivateDnsZone').outputs.resourceId.value)), createObject('value', createArray()))]", "logAnalyticsWorkspaceResourceId": { @@ -43501,7 +43584,7 @@ "_generator": { "name": "bicep", "version": "0.36.1.42791", - "templateHash": "16919614201922288466" + "templateHash": "2348080591288311162" } }, "definitions": { @@ -43806,6 +43889,12 @@ "metadata": { "description": "Optional. Tags to be applied to the resources." } + }, + "networkAcls": { + "type": "object", + "metadata": { + "description": "Optional. A collection of rules governing the accessibility from specific network locations." + } } }, "variables": { @@ -43846,6 +43935,9 @@ "kind": { "value": "[parameters('kind')]" }, + "allowProjectManagement": { + "value": true + }, "managedIdentities": { "value": { "systemAssigned": true @@ -43871,6 +43963,9 @@ "roleAssignments": { "value": "[parameters('roleAssignments')]" }, + "networkAcls": { + "value": "[parameters('networkAcls')]" + }, "privateEndpoints": "[if(parameters('networkIsolation'), createObject('value', createArray(createObject('privateDnsZoneGroup', createObject('privateDnsZoneGroupConfigs', variables('privateDnsZones')), 'subnetResourceId', parameters('virtualNetworkSubnetResourceId')))), createObject('value', createArray()))]" }, "template": { @@ -43880,8 +43975,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "616090829858030869" + "version": "0.36.1.42791", + "templateHash": "16135659971302525380" }, "name": "Cognitive Services", "description": "This module deploys a Cognitive Service." @@ -43947,112 +44042,110 @@ } }, "metadata": { - "__bicep_export!": true + "__bicep_export!": true, + "description": "The type for the private endpoint output." } }, - "deploymentsType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Specify the name of cognitive service account deployment." - } - }, - "model": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of Cognitive Services account deployment model." - } - }, - "format": { - "type": "string", - "metadata": { - "description": "Required. The format of Cognitive Services account deployment model." - } - }, - "version": { - "type": "string", - "metadata": { - "description": "Required. The version of Cognitive Services account deployment model." - } + "deploymentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of cognitive service account deployment." + } + }, + "model": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of Cognitive Services account deployment model." } }, - "metadata": { - "description": "Required. Properties of Cognitive Services account deployment model." - } - }, - "sku": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the resource model definition representing SKU." - } - }, - "capacity": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The capacity of the resource model definition representing SKU." - } - }, - "tier": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The tier of the resource model definition representing SKU." - } - }, - "size": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The size of the resource model definition representing SKU." - } - }, - "family": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The family of the resource model definition representing SKU." - } + "format": { + "type": "string", + "metadata": { + "description": "Required. The format of Cognitive Services account deployment model." } }, - "nullable": true, - "metadata": { - "description": "Optional. The resource model definition representing SKU." + "version": { + "type": "string", + "metadata": { + "description": "Required. The version of Cognitive Services account deployment model." + } } }, - "raiPolicyName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of RAI policy." + "metadata": { + "description": "Required. Properties of Cognitive Services account deployment model." + } + }, + "sku": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource model definition representing SKU." + } + }, + "capacity": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The capacity of the resource model definition representing SKU." + } + }, + "tier": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The tier of the resource model definition representing SKU." + } + }, + "size": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The size of the resource model definition representing SKU." + } + }, + "family": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The family of the resource model definition representing SKU." + } } }, - "versionUpgradeOption": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The version upgrade option." - } + "nullable": true, + "metadata": { + "description": "Optional. The resource model definition representing SKU." + } + }, + "raiPolicyName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of RAI policy." + } + }, + "versionUpgradeOption": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The version upgrade option." } } }, - "nullable": true, "metadata": { - "__bicep_export!": true + "__bicep_export!": true, + "description": "The type for a cognitive services account deployment." } }, - "endpointsType": { + "endpointType": { "type": "object", "properties": { "name": { @@ -44070,9 +44163,9 @@ } } }, - "nullable": true, "metadata": { - "__bicep_export!": true + "__bicep_export!": true, + "description": "The type for a cognitive services account endpoint." } }, "secretsExportConfigurationType": { @@ -44100,7 +44193,8 @@ } }, "metadata": { - "__bicep_export!": true + "__bicep_export!": true, + "description": "The type of the secrets exported to the provided Key Vault." } }, "_1.privateEndpointCustomDnsConfigType": { @@ -44125,7 +44219,7 @@ }, "metadata": { "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -44167,7 +44261,7 @@ }, "metadata": { "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -44208,7 +44302,7 @@ }, "metadata": { "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -44237,7 +44331,7 @@ "metadata": { "description": "An AVM-aligned type for the output of the secret set via the secrets export feature.", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -44274,7 +44368,7 @@ "metadata": { "description": "An AVM-aligned type for a customer-managed key. To be used if the resource type does not support auto-rotation of the customer-managed key.", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -44396,7 +44490,7 @@ "metadata": { "description": "An AVM-aligned type for a diagnostic setting. To be used if both logs & metrics are supported by the resource provider.", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -44426,7 +44520,7 @@ "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.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -44454,7 +44548,7 @@ "metadata": { "description": "An AVM-aligned type for a managed identity configuration. To be used if both a system-assigned & user-assigned identities are supported by the resource provider.", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -44495,6 +44589,13 @@ "description": "Required. Resource ID of the subnet where the endpoint needs to be created." } }, + "resourceGroupResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the Resource Group the Private Endpoint will be created in. If not specified, the Resource Group of the provided Virtual Network Subnet is used." + } + }, "privateDnsZoneGroup": { "$ref": "#/definitions/_1.privateEndpointPrivateDnsZoneGroupType", "nullable": true, @@ -44584,19 +44685,12 @@ "metadata": { "description": "Optional. Enable/Disable usage telemetry for module." } - }, - "resourceGroupName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Specify if you want to deploy the Private Endpoint into a different Resource Group than the main resource." - } } }, "metadata": { "description": "An AVM-aligned type for a private endpoint. To be used if the private endpoint's default service / groupId can be assumed (i.e., for services that only have one Private Endpoint type like 'vault' for key vault).", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -44671,7 +44765,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.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -44687,7 +44781,7 @@ "metadata": { "description": "A map of the exported secrets", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } } @@ -44911,7 +45005,11 @@ } }, "deployments": { - "$ref": "#/definitions/deploymentsType", + "type": "array", + "items": { + "$ref": "#/definitions/deploymentType" + }, + "nullable": true, "metadata": { "description": "Optional. Array of deployments about cognitive service accounts to create." } @@ -44922,6 +45020,13 @@ "metadata": { "description": "Optional. Key vault reference and secret settings for the module's secrets export." } + }, + "allowProjectManagement": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable/Disable project management feature for AI Foundry." + } } }, "variables": { @@ -44960,6 +45065,7 @@ "Cognitive Services Speech Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0e75ca1e-0464-4b4d-8b93-68208a576181')]", "Cognitive Services Speech User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f2dc8367-1007-4938-bd23-fe263f013447')]", "Cognitive Services User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a97b65f3-24c7-4388-baec-2e87135dc908')]", + "Azure AI Developer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '64702f94-c441-49e6-a78b-ef80e0188fee')]", "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", @@ -44972,7 +45078,7 @@ "condition": "[and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), not(empty(tryGet(parameters('customerManagedKey'), 'keyName')))))]", "existing": true, "type": "Microsoft.KeyVault/vaults/keys", - "apiVersion": "2023-02-01", + "apiVersion": "2023-07-01", "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[2]]", "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[4]]", "name": "[format('{0}/{1}', last(split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')), tryGet(parameters('customerManagedKey'), 'keyName'))]" @@ -44981,7 +45087,7 @@ "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2024-03-01", - "name": "[format('46d3xbcp.res.cognitiveservices-account.{0}.{1}', replace('0.10.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "name": "[format('46d3xbcp.res.cognitiveservices-account.{0}.{1}', replace('0.11.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -45001,7 +45107,7 @@ "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId')))]", "existing": true, "type": "Microsoft.KeyVault/vaults", - "apiVersion": "2023-02-01", + "apiVersion": "2023-07-01", "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[2]]", "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[4]]", "name": "[last(split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/'))]" @@ -45010,14 +45116,14 @@ "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId')))]", "existing": true, "type": "Microsoft.ManagedIdentity/userAssignedIdentities", - "apiVersion": "2023-01-31", + "apiVersion": "2025-01-31-preview", "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/')[2]]", "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/')[4]]", "name": "[last(split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/'))]" }, "cognitiveService": { "type": "Microsoft.CognitiveServices/accounts", - "apiVersion": "2023-05-01", + "apiVersion": "2025-04-01-preview", "name": "[parameters('name')]", "kind": "[parameters('kind')]", "identity": "[variables('identity')]", @@ -45027,6 +45133,7 @@ "name": "[parameters('sku')]" }, "properties": { + "allowProjectManagement": "[parameters('allowProjectManagement')]", "customSubDomainName": "[parameters('customSubDomainName')]", "networkAcls": "[if(not(empty(coalesce(parameters('networkAcls'), createObject()))), createObject('defaultAction', tryGet(parameters('networkAcls'), 'defaultAction'), 'virtualNetworkRules', coalesce(tryGet(parameters('networkAcls'), 'virtualNetworkRules'), createArray()), 'ipRules', coalesce(tryGet(parameters('networkAcls'), 'ipRules'), createArray())), null())]", "publicNetworkAccess": "[if(not(equals(parameters('publicNetworkAccess'), null())), parameters('publicNetworkAccess'), if(not(empty(parameters('networkAcls'))), 'Enabled', 'Disabled'))]", @@ -45041,8 +45148,8 @@ "dynamicThrottlingEnabled": "[parameters('dynamicThrottlingEnabled')]" }, "dependsOn": [ - "cMKKeyVault::cMKKey", "cMKKeyVault", + "cMKKeyVault::cMKKey", "cMKUserAssignedIdentity" ] }, @@ -45054,7 +45161,7 @@ "batchSize": 1 }, "type": "Microsoft.CognitiveServices/accounts/deployments", - "apiVersion": "2023-05-01", + "apiVersion": "2025-04-01-preview", "name": "[format('{0}/{1}', parameters('name'), coalesce(tryGet(coalesce(parameters('deployments'), createArray())[copyIndex()], 'name'), format('{0}-deployments', parameters('name'))))]", "properties": { "model": "[coalesce(parameters('deployments'), createArray())[copyIndex()].model]", @@ -45205,8 +45312,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "15954548978129725136" + "version": "0.34.44.8038", + "templateHash": "12389807800450456797" }, "name": "Private Endpoints", "description": "This module deploys a Private Endpoint." @@ -45615,7 +45722,7 @@ "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2024-03-01", - "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.10.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.11.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -45633,7 +45740,7 @@ }, "privateEndpoint": { "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2023-11-01", + "apiVersion": "2024-05-01", "name": "[parameters('name')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", @@ -45721,8 +45828,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "5440815542537978381" + "version": "0.34.44.8038", + "templateHash": "13997305779829540948" }, "name": "Private Endpoint Private DNS Zone Groups", "description": "This module deploys a Private Endpoint Private DNS Zone Group." @@ -45794,12 +45901,12 @@ "privateEndpoint": { "existing": true, "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2023-11-01", + "apiVersion": "2024-05-01", "name": "[parameters('privateEndpointName')]" }, "privateDnsZoneGroup": { "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2023-11-01", + "apiVersion": "2024-05-01", "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", "properties": { "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" @@ -45863,7 +45970,7 @@ "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('privateEndpoint', '2023-11-01', 'full').location]" + "value": "[reference('privateEndpoint', '2024-05-01', 'full').location]" }, "customDnsConfigs": { "type": "array", @@ -45917,7 +46024,7 @@ "value": "[last(split(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '/'))]" }, "secretsToSet": { - "value": "[union(createArray(), if(contains(parameters('secretsExportConfiguration'), 'accessKey1Name'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'accessKey1Name'), 'value', listKeys(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), '2023-05-01').key1)), createArray()), if(contains(parameters('secretsExportConfiguration'), 'accessKey2Name'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'accessKey2Name'), 'value', listKeys(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), '2023-05-01').key2)), createArray()))]" + "value": "[union(createArray(), if(contains(parameters('secretsExportConfiguration'), 'accessKey1Name'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'accessKey1Name'), 'value', listKeys('cognitiveService', '2025-04-01-preview').key1)), createArray()), if(contains(parameters('secretsExportConfiguration'), 'accessKey2Name'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'accessKey2Name'), 'value', listKeys('cognitiveService', '2025-04-01-preview').key2)), createArray()))]" } }, "template": { @@ -45927,8 +46034,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "1340925512665498953" + "version": "0.36.1.42791", + "templateHash": "1200612323329026557" } }, "definitions": { @@ -45957,7 +46064,7 @@ "metadata": { "description": "An AVM-aligned type for the output of the secret set via the secrets export feature.", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -45980,7 +46087,7 @@ "metadata": { "description": "An AVM-aligned type for the secret to set via the secrets export feature.", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } } @@ -46006,7 +46113,7 @@ "keyVault": { "existing": true, "type": "Microsoft.KeyVault/vaults", - "apiVersion": "2022-07-01", + "apiVersion": "2023-07-01", "name": "[parameters('keyVaultName')]" }, "secrets": { @@ -46078,7 +46185,7 @@ "value": "[reference('cognitiveService').endpoint]" }, "endpoints": { - "$ref": "#/definitions/endpointsType", + "$ref": "#/definitions/endpointType", "metadata": { "description": "All endpoints available for the cognitive services account, types depends on the cognitive service kind." }, @@ -46090,14 +46197,14 @@ "metadata": { "description": "The principal ID of the system assigned identity." }, - "value": "[tryGet(tryGet(reference('cognitiveService', '2023-05-01', 'full'), 'identity'), 'principalId')]" + "value": "[tryGet(tryGet(reference('cognitiveService', '2025-04-01-preview', 'full'), 'identity'), 'principalId')]" }, "location": { "type": "string", "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('cognitiveService', '2023-05-01', 'full').location]" + "value": "[reference('cognitiveService', '2025-04-01-preview', 'full').location]" }, "exportedSecrets": { "$ref": "#/definitions/secretsOutputType", @@ -46200,6 +46307,9 @@ "networkIsolation": { "value": "[parameters('networkIsolation')]" }, + "networkAcls": { + "value": "[parameters('networkAcls')]" + }, "virtualNetworkSubnetResourceId": "[if(parameters('networkIsolation'), createObject('value', parameters('virtualNetworkSubnetResourceId')), createObject('value', ''))]", "privateDnsZonesResourceIds": "[if(parameters('networkIsolation'), createObject('value', createArray(reference('cognitiveServicesPrivateDnsZone').outputs.resourceId.value)), createObject('value', createArray()))]", "logAnalyticsWorkspaceResourceId": { @@ -46217,7 +46327,7 @@ "_generator": { "name": "bicep", "version": "0.36.1.42791", - "templateHash": "16919614201922288466" + "templateHash": "2348080591288311162" } }, "definitions": { @@ -46522,6 +46632,12 @@ "metadata": { "description": "Optional. Tags to be applied to the resources." } + }, + "networkAcls": { + "type": "object", + "metadata": { + "description": "Optional. A collection of rules governing the accessibility from specific network locations." + } } }, "variables": { @@ -46562,6 +46678,9 @@ "kind": { "value": "[parameters('kind')]" }, + "allowProjectManagement": { + "value": true + }, "managedIdentities": { "value": { "systemAssigned": true @@ -46587,6 +46706,9 @@ "roleAssignments": { "value": "[parameters('roleAssignments')]" }, + "networkAcls": { + "value": "[parameters('networkAcls')]" + }, "privateEndpoints": "[if(parameters('networkIsolation'), createObject('value', createArray(createObject('privateDnsZoneGroup', createObject('privateDnsZoneGroupConfigs', variables('privateDnsZones')), 'subnetResourceId', parameters('virtualNetworkSubnetResourceId')))), createObject('value', createArray()))]" }, "template": { @@ -46596,8 +46718,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "616090829858030869" + "version": "0.36.1.42791", + "templateHash": "16135659971302525380" }, "name": "Cognitive Services", "description": "This module deploys a Cognitive Service." @@ -46663,112 +46785,110 @@ } }, "metadata": { - "__bicep_export!": true + "__bicep_export!": true, + "description": "The type for the private endpoint output." } }, - "deploymentsType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Specify the name of cognitive service account deployment." - } - }, - "model": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of Cognitive Services account deployment model." - } - }, - "format": { - "type": "string", - "metadata": { - "description": "Required. The format of Cognitive Services account deployment model." - } - }, - "version": { - "type": "string", - "metadata": { - "description": "Required. The version of Cognitive Services account deployment model." - } + "deploymentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of cognitive service account deployment." + } + }, + "model": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of Cognitive Services account deployment model." } }, - "metadata": { - "description": "Required. Properties of Cognitive Services account deployment model." - } - }, - "sku": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the resource model definition representing SKU." - } - }, - "capacity": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The capacity of the resource model definition representing SKU." - } - }, - "tier": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The tier of the resource model definition representing SKU." - } - }, - "size": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The size of the resource model definition representing SKU." - } - }, - "family": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The family of the resource model definition representing SKU." - } + "format": { + "type": "string", + "metadata": { + "description": "Required. The format of Cognitive Services account deployment model." } }, - "nullable": true, - "metadata": { - "description": "Optional. The resource model definition representing SKU." + "version": { + "type": "string", + "metadata": { + "description": "Required. The version of Cognitive Services account deployment model." + } } }, - "raiPolicyName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of RAI policy." + "metadata": { + "description": "Required. Properties of Cognitive Services account deployment model." + } + }, + "sku": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource model definition representing SKU." + } + }, + "capacity": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The capacity of the resource model definition representing SKU." + } + }, + "tier": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The tier of the resource model definition representing SKU." + } + }, + "size": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The size of the resource model definition representing SKU." + } + }, + "family": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The family of the resource model definition representing SKU." + } } }, - "versionUpgradeOption": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The version upgrade option." - } + "nullable": true, + "metadata": { + "description": "Optional. The resource model definition representing SKU." + } + }, + "raiPolicyName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of RAI policy." + } + }, + "versionUpgradeOption": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The version upgrade option." } } }, - "nullable": true, "metadata": { - "__bicep_export!": true + "__bicep_export!": true, + "description": "The type for a cognitive services account deployment." } }, - "endpointsType": { + "endpointType": { "type": "object", "properties": { "name": { @@ -46786,9 +46906,9 @@ } } }, - "nullable": true, "metadata": { - "__bicep_export!": true + "__bicep_export!": true, + "description": "The type for a cognitive services account endpoint." } }, "secretsExportConfigurationType": { @@ -46816,7 +46936,8 @@ } }, "metadata": { - "__bicep_export!": true + "__bicep_export!": true, + "description": "The type of the secrets exported to the provided Key Vault." } }, "_1.privateEndpointCustomDnsConfigType": { @@ -46841,7 +46962,7 @@ }, "metadata": { "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -46883,7 +47004,7 @@ }, "metadata": { "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -46924,7 +47045,7 @@ }, "metadata": { "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -46953,7 +47074,7 @@ "metadata": { "description": "An AVM-aligned type for the output of the secret set via the secrets export feature.", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -46990,7 +47111,7 @@ "metadata": { "description": "An AVM-aligned type for a customer-managed key. To be used if the resource type does not support auto-rotation of the customer-managed key.", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -47112,7 +47233,7 @@ "metadata": { "description": "An AVM-aligned type for a diagnostic setting. To be used if both logs & metrics are supported by the resource provider.", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -47142,7 +47263,7 @@ "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.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -47170,7 +47291,7 @@ "metadata": { "description": "An AVM-aligned type for a managed identity configuration. To be used if both a system-assigned & user-assigned identities are supported by the resource provider.", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -47211,6 +47332,13 @@ "description": "Required. Resource ID of the subnet where the endpoint needs to be created." } }, + "resourceGroupResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the Resource Group the Private Endpoint will be created in. If not specified, the Resource Group of the provided Virtual Network Subnet is used." + } + }, "privateDnsZoneGroup": { "$ref": "#/definitions/_1.privateEndpointPrivateDnsZoneGroupType", "nullable": true, @@ -47300,19 +47428,12 @@ "metadata": { "description": "Optional. Enable/Disable usage telemetry for module." } - }, - "resourceGroupName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Specify if you want to deploy the Private Endpoint into a different Resource Group than the main resource." - } } }, "metadata": { "description": "An AVM-aligned type for a private endpoint. To be used if the private endpoint's default service / groupId can be assumed (i.e., for services that only have one Private Endpoint type like 'vault' for key vault).", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -47387,7 +47508,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.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -47403,7 +47524,7 @@ "metadata": { "description": "A map of the exported secrets", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } } @@ -47627,7 +47748,11 @@ } }, "deployments": { - "$ref": "#/definitions/deploymentsType", + "type": "array", + "items": { + "$ref": "#/definitions/deploymentType" + }, + "nullable": true, "metadata": { "description": "Optional. Array of deployments about cognitive service accounts to create." } @@ -47638,6 +47763,13 @@ "metadata": { "description": "Optional. Key vault reference and secret settings for the module's secrets export." } + }, + "allowProjectManagement": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable/Disable project management feature for AI Foundry." + } } }, "variables": { @@ -47676,6 +47808,7 @@ "Cognitive Services Speech Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0e75ca1e-0464-4b4d-8b93-68208a576181')]", "Cognitive Services Speech User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f2dc8367-1007-4938-bd23-fe263f013447')]", "Cognitive Services User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a97b65f3-24c7-4388-baec-2e87135dc908')]", + "Azure AI Developer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '64702f94-c441-49e6-a78b-ef80e0188fee')]", "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", @@ -47688,7 +47821,7 @@ "condition": "[and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), not(empty(tryGet(parameters('customerManagedKey'), 'keyName')))))]", "existing": true, "type": "Microsoft.KeyVault/vaults/keys", - "apiVersion": "2023-02-01", + "apiVersion": "2023-07-01", "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[2]]", "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[4]]", "name": "[format('{0}/{1}', last(split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')), tryGet(parameters('customerManagedKey'), 'keyName'))]" @@ -47697,7 +47830,7 @@ "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2024-03-01", - "name": "[format('46d3xbcp.res.cognitiveservices-account.{0}.{1}', replace('0.10.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "name": "[format('46d3xbcp.res.cognitiveservices-account.{0}.{1}', replace('0.11.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -47717,7 +47850,7 @@ "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId')))]", "existing": true, "type": "Microsoft.KeyVault/vaults", - "apiVersion": "2023-02-01", + "apiVersion": "2023-07-01", "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[2]]", "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[4]]", "name": "[last(split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/'))]" @@ -47726,14 +47859,14 @@ "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId')))]", "existing": true, "type": "Microsoft.ManagedIdentity/userAssignedIdentities", - "apiVersion": "2023-01-31", + "apiVersion": "2025-01-31-preview", "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/')[2]]", "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/')[4]]", "name": "[last(split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/'))]" }, "cognitiveService": { "type": "Microsoft.CognitiveServices/accounts", - "apiVersion": "2023-05-01", + "apiVersion": "2025-04-01-preview", "name": "[parameters('name')]", "kind": "[parameters('kind')]", "identity": "[variables('identity')]", @@ -47743,6 +47876,7 @@ "name": "[parameters('sku')]" }, "properties": { + "allowProjectManagement": "[parameters('allowProjectManagement')]", "customSubDomainName": "[parameters('customSubDomainName')]", "networkAcls": "[if(not(empty(coalesce(parameters('networkAcls'), createObject()))), createObject('defaultAction', tryGet(parameters('networkAcls'), 'defaultAction'), 'virtualNetworkRules', coalesce(tryGet(parameters('networkAcls'), 'virtualNetworkRules'), createArray()), 'ipRules', coalesce(tryGet(parameters('networkAcls'), 'ipRules'), createArray())), null())]", "publicNetworkAccess": "[if(not(equals(parameters('publicNetworkAccess'), null())), parameters('publicNetworkAccess'), if(not(empty(parameters('networkAcls'))), 'Enabled', 'Disabled'))]", @@ -47757,8 +47891,8 @@ "dynamicThrottlingEnabled": "[parameters('dynamicThrottlingEnabled')]" }, "dependsOn": [ - "cMKKeyVault::cMKKey", "cMKKeyVault", + "cMKKeyVault::cMKKey", "cMKUserAssignedIdentity" ] }, @@ -47770,7 +47904,7 @@ "batchSize": 1 }, "type": "Microsoft.CognitiveServices/accounts/deployments", - "apiVersion": "2023-05-01", + "apiVersion": "2025-04-01-preview", "name": "[format('{0}/{1}', parameters('name'), coalesce(tryGet(coalesce(parameters('deployments'), createArray())[copyIndex()], 'name'), format('{0}-deployments', parameters('name'))))]", "properties": { "model": "[coalesce(parameters('deployments'), createArray())[copyIndex()].model]", @@ -47921,8 +48055,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "15954548978129725136" + "version": "0.34.44.8038", + "templateHash": "12389807800450456797" }, "name": "Private Endpoints", "description": "This module deploys a Private Endpoint." @@ -48331,7 +48465,7 @@ "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2024-03-01", - "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.10.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.11.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -48349,7 +48483,7 @@ }, "privateEndpoint": { "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2023-11-01", + "apiVersion": "2024-05-01", "name": "[parameters('name')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", @@ -48437,8 +48571,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "5440815542537978381" + "version": "0.34.44.8038", + "templateHash": "13997305779829540948" }, "name": "Private Endpoint Private DNS Zone Groups", "description": "This module deploys a Private Endpoint Private DNS Zone Group." @@ -48510,12 +48644,12 @@ "privateEndpoint": { "existing": true, "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2023-11-01", + "apiVersion": "2024-05-01", "name": "[parameters('privateEndpointName')]" }, "privateDnsZoneGroup": { "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2023-11-01", + "apiVersion": "2024-05-01", "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", "properties": { "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" @@ -48579,7 +48713,7 @@ "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('privateEndpoint', '2023-11-01', 'full').location]" + "value": "[reference('privateEndpoint', '2024-05-01', 'full').location]" }, "customDnsConfigs": { "type": "array", @@ -48633,7 +48767,7 @@ "value": "[last(split(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '/'))]" }, "secretsToSet": { - "value": "[union(createArray(), if(contains(parameters('secretsExportConfiguration'), 'accessKey1Name'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'accessKey1Name'), 'value', listKeys(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), '2023-05-01').key1)), createArray()), if(contains(parameters('secretsExportConfiguration'), 'accessKey2Name'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'accessKey2Name'), 'value', listKeys(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), '2023-05-01').key2)), createArray()))]" + "value": "[union(createArray(), if(contains(parameters('secretsExportConfiguration'), 'accessKey1Name'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'accessKey1Name'), 'value', listKeys('cognitiveService', '2025-04-01-preview').key1)), createArray()), if(contains(parameters('secretsExportConfiguration'), 'accessKey2Name'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'accessKey2Name'), 'value', listKeys('cognitiveService', '2025-04-01-preview').key2)), createArray()))]" } }, "template": { @@ -48643,8 +48777,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "1340925512665498953" + "version": "0.36.1.42791", + "templateHash": "1200612323329026557" } }, "definitions": { @@ -48673,7 +48807,7 @@ "metadata": { "description": "An AVM-aligned type for the output of the secret set via the secrets export feature.", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -48696,7 +48830,7 @@ "metadata": { "description": "An AVM-aligned type for the secret to set via the secrets export feature.", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } } @@ -48722,7 +48856,7 @@ "keyVault": { "existing": true, "type": "Microsoft.KeyVault/vaults", - "apiVersion": "2022-07-01", + "apiVersion": "2023-07-01", "name": "[parameters('keyVaultName')]" }, "secrets": { @@ -48794,7 +48928,7 @@ "value": "[reference('cognitiveService').endpoint]" }, "endpoints": { - "$ref": "#/definitions/endpointsType", + "$ref": "#/definitions/endpointType", "metadata": { "description": "All endpoints available for the cognitive services account, types depends on the cognitive service kind." }, @@ -48806,14 +48940,14 @@ "metadata": { "description": "The principal ID of the system assigned identity." }, - "value": "[tryGet(tryGet(reference('cognitiveService', '2023-05-01', 'full'), 'identity'), 'principalId')]" + "value": "[tryGet(tryGet(reference('cognitiveService', '2025-04-01-preview', 'full'), 'identity'), 'principalId')]" }, "location": { "type": "string", "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('cognitiveService', '2023-05-01', 'full').location]" + "value": "[reference('cognitiveService', '2025-04-01-preview', 'full').location]" }, "exportedSecrets": { "$ref": "#/definitions/secretsOutputType", @@ -48913,6 +49047,9 @@ "networkIsolation": { "value": "[parameters('networkIsolation')]" }, + "networkAcls": { + "value": "[parameters('networkAcls')]" + }, "virtualNetworkSubnetResourceId": "[if(parameters('networkIsolation'), createObject('value', parameters('virtualNetworkSubnetResourceId')), createObject('value', ''))]", "privateDnsZonesResourceIds": "[if(parameters('networkIsolation'), createObject('value', createArray(reference('cognitiveServicesPrivateDnsZone').outputs.resourceId.value)), createObject('value', createArray()))]", "logAnalyticsWorkspaceResourceId": { @@ -48930,7 +49067,7 @@ "_generator": { "name": "bicep", "version": "0.36.1.42791", - "templateHash": "16919614201922288466" + "templateHash": "2348080591288311162" } }, "definitions": { @@ -49235,6 +49372,12 @@ "metadata": { "description": "Optional. Tags to be applied to the resources." } + }, + "networkAcls": { + "type": "object", + "metadata": { + "description": "Optional. A collection of rules governing the accessibility from specific network locations." + } } }, "variables": { @@ -49275,6 +49418,9 @@ "kind": { "value": "[parameters('kind')]" }, + "allowProjectManagement": { + "value": true + }, "managedIdentities": { "value": { "systemAssigned": true @@ -49300,6 +49446,9 @@ "roleAssignments": { "value": "[parameters('roleAssignments')]" }, + "networkAcls": { + "value": "[parameters('networkAcls')]" + }, "privateEndpoints": "[if(parameters('networkIsolation'), createObject('value', createArray(createObject('privateDnsZoneGroup', createObject('privateDnsZoneGroupConfigs', variables('privateDnsZones')), 'subnetResourceId', parameters('virtualNetworkSubnetResourceId')))), createObject('value', createArray()))]" }, "template": { @@ -49309,8 +49458,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "616090829858030869" + "version": "0.36.1.42791", + "templateHash": "16135659971302525380" }, "name": "Cognitive Services", "description": "This module deploys a Cognitive Service." @@ -49376,112 +49525,110 @@ } }, "metadata": { - "__bicep_export!": true + "__bicep_export!": true, + "description": "The type for the private endpoint output." } }, - "deploymentsType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Specify the name of cognitive service account deployment." - } - }, - "model": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of Cognitive Services account deployment model." - } - }, - "format": { - "type": "string", - "metadata": { - "description": "Required. The format of Cognitive Services account deployment model." - } - }, - "version": { - "type": "string", - "metadata": { - "description": "Required. The version of Cognitive Services account deployment model." - } + "deploymentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of cognitive service account deployment." + } + }, + "model": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of Cognitive Services account deployment model." } }, - "metadata": { - "description": "Required. Properties of Cognitive Services account deployment model." - } - }, - "sku": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the resource model definition representing SKU." - } - }, - "capacity": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The capacity of the resource model definition representing SKU." - } - }, - "tier": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The tier of the resource model definition representing SKU." - } - }, - "size": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The size of the resource model definition representing SKU." - } - }, - "family": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The family of the resource model definition representing SKU." - } + "format": { + "type": "string", + "metadata": { + "description": "Required. The format of Cognitive Services account deployment model." } }, - "nullable": true, - "metadata": { - "description": "Optional. The resource model definition representing SKU." + "version": { + "type": "string", + "metadata": { + "description": "Required. The version of Cognitive Services account deployment model." + } } }, - "raiPolicyName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of RAI policy." + "metadata": { + "description": "Required. Properties of Cognitive Services account deployment model." + } + }, + "sku": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource model definition representing SKU." + } + }, + "capacity": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The capacity of the resource model definition representing SKU." + } + }, + "tier": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The tier of the resource model definition representing SKU." + } + }, + "size": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The size of the resource model definition representing SKU." + } + }, + "family": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The family of the resource model definition representing SKU." + } } }, - "versionUpgradeOption": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The version upgrade option." - } + "nullable": true, + "metadata": { + "description": "Optional. The resource model definition representing SKU." + } + }, + "raiPolicyName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of RAI policy." + } + }, + "versionUpgradeOption": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The version upgrade option." } } }, - "nullable": true, "metadata": { - "__bicep_export!": true + "__bicep_export!": true, + "description": "The type for a cognitive services account deployment." } }, - "endpointsType": { + "endpointType": { "type": "object", "properties": { "name": { @@ -49499,9 +49646,9 @@ } } }, - "nullable": true, "metadata": { - "__bicep_export!": true + "__bicep_export!": true, + "description": "The type for a cognitive services account endpoint." } }, "secretsExportConfigurationType": { @@ -49529,7 +49676,8 @@ } }, "metadata": { - "__bicep_export!": true + "__bicep_export!": true, + "description": "The type of the secrets exported to the provided Key Vault." } }, "_1.privateEndpointCustomDnsConfigType": { @@ -49554,7 +49702,7 @@ }, "metadata": { "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -49596,7 +49744,7 @@ }, "metadata": { "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -49637,7 +49785,7 @@ }, "metadata": { "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -49666,7 +49814,7 @@ "metadata": { "description": "An AVM-aligned type for the output of the secret set via the secrets export feature.", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -49703,7 +49851,7 @@ "metadata": { "description": "An AVM-aligned type for a customer-managed key. To be used if the resource type does not support auto-rotation of the customer-managed key.", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -49825,7 +49973,7 @@ "metadata": { "description": "An AVM-aligned type for a diagnostic setting. To be used if both logs & metrics are supported by the resource provider.", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -49855,7 +50003,7 @@ "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.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -49883,7 +50031,7 @@ "metadata": { "description": "An AVM-aligned type for a managed identity configuration. To be used if both a system-assigned & user-assigned identities are supported by the resource provider.", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -49924,6 +50072,13 @@ "description": "Required. Resource ID of the subnet where the endpoint needs to be created." } }, + "resourceGroupResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the Resource Group the Private Endpoint will be created in. If not specified, the Resource Group of the provided Virtual Network Subnet is used." + } + }, "privateDnsZoneGroup": { "$ref": "#/definitions/_1.privateEndpointPrivateDnsZoneGroupType", "nullable": true, @@ -50013,19 +50168,12 @@ "metadata": { "description": "Optional. Enable/Disable usage telemetry for module." } - }, - "resourceGroupName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Specify if you want to deploy the Private Endpoint into a different Resource Group than the main resource." - } } }, "metadata": { "description": "An AVM-aligned type for a private endpoint. To be used if the private endpoint's default service / groupId can be assumed (i.e., for services that only have one Private Endpoint type like 'vault' for key vault).", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -50100,7 +50248,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.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -50116,7 +50264,7 @@ "metadata": { "description": "A map of the exported secrets", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } } @@ -50340,7 +50488,11 @@ } }, "deployments": { - "$ref": "#/definitions/deploymentsType", + "type": "array", + "items": { + "$ref": "#/definitions/deploymentType" + }, + "nullable": true, "metadata": { "description": "Optional. Array of deployments about cognitive service accounts to create." } @@ -50351,6 +50503,13 @@ "metadata": { "description": "Optional. Key vault reference and secret settings for the module's secrets export." } + }, + "allowProjectManagement": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable/Disable project management feature for AI Foundry." + } } }, "variables": { @@ -50389,6 +50548,7 @@ "Cognitive Services Speech Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0e75ca1e-0464-4b4d-8b93-68208a576181')]", "Cognitive Services Speech User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f2dc8367-1007-4938-bd23-fe263f013447')]", "Cognitive Services User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a97b65f3-24c7-4388-baec-2e87135dc908')]", + "Azure AI Developer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '64702f94-c441-49e6-a78b-ef80e0188fee')]", "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", @@ -50401,7 +50561,7 @@ "condition": "[and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), not(empty(tryGet(parameters('customerManagedKey'), 'keyName')))))]", "existing": true, "type": "Microsoft.KeyVault/vaults/keys", - "apiVersion": "2023-02-01", + "apiVersion": "2023-07-01", "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[2]]", "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[4]]", "name": "[format('{0}/{1}', last(split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')), tryGet(parameters('customerManagedKey'), 'keyName'))]" @@ -50410,7 +50570,7 @@ "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2024-03-01", - "name": "[format('46d3xbcp.res.cognitiveservices-account.{0}.{1}', replace('0.10.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "name": "[format('46d3xbcp.res.cognitiveservices-account.{0}.{1}', replace('0.11.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -50430,7 +50590,7 @@ "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId')))]", "existing": true, "type": "Microsoft.KeyVault/vaults", - "apiVersion": "2023-02-01", + "apiVersion": "2023-07-01", "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[2]]", "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[4]]", "name": "[last(split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/'))]" @@ -50439,14 +50599,14 @@ "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId')))]", "existing": true, "type": "Microsoft.ManagedIdentity/userAssignedIdentities", - "apiVersion": "2023-01-31", + "apiVersion": "2025-01-31-preview", "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/')[2]]", "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/')[4]]", "name": "[last(split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/'))]" }, "cognitiveService": { "type": "Microsoft.CognitiveServices/accounts", - "apiVersion": "2023-05-01", + "apiVersion": "2025-04-01-preview", "name": "[parameters('name')]", "kind": "[parameters('kind')]", "identity": "[variables('identity')]", @@ -50456,6 +50616,7 @@ "name": "[parameters('sku')]" }, "properties": { + "allowProjectManagement": "[parameters('allowProjectManagement')]", "customSubDomainName": "[parameters('customSubDomainName')]", "networkAcls": "[if(not(empty(coalesce(parameters('networkAcls'), createObject()))), createObject('defaultAction', tryGet(parameters('networkAcls'), 'defaultAction'), 'virtualNetworkRules', coalesce(tryGet(parameters('networkAcls'), 'virtualNetworkRules'), createArray()), 'ipRules', coalesce(tryGet(parameters('networkAcls'), 'ipRules'), createArray())), null())]", "publicNetworkAccess": "[if(not(equals(parameters('publicNetworkAccess'), null())), parameters('publicNetworkAccess'), if(not(empty(parameters('networkAcls'))), 'Enabled', 'Disabled'))]", @@ -50470,8 +50631,8 @@ "dynamicThrottlingEnabled": "[parameters('dynamicThrottlingEnabled')]" }, "dependsOn": [ - "cMKKeyVault::cMKKey", "cMKKeyVault", + "cMKKeyVault::cMKKey", "cMKUserAssignedIdentity" ] }, @@ -50483,7 +50644,7 @@ "batchSize": 1 }, "type": "Microsoft.CognitiveServices/accounts/deployments", - "apiVersion": "2023-05-01", + "apiVersion": "2025-04-01-preview", "name": "[format('{0}/{1}', parameters('name'), coalesce(tryGet(coalesce(parameters('deployments'), createArray())[copyIndex()], 'name'), format('{0}-deployments', parameters('name'))))]", "properties": { "model": "[coalesce(parameters('deployments'), createArray())[copyIndex()].model]", @@ -50634,8 +50795,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "15954548978129725136" + "version": "0.34.44.8038", + "templateHash": "12389807800450456797" }, "name": "Private Endpoints", "description": "This module deploys a Private Endpoint." @@ -51044,7 +51205,7 @@ "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2024-03-01", - "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.10.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.11.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -51062,7 +51223,7 @@ }, "privateEndpoint": { "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2023-11-01", + "apiVersion": "2024-05-01", "name": "[parameters('name')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", @@ -51150,8 +51311,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "5440815542537978381" + "version": "0.34.44.8038", + "templateHash": "13997305779829540948" }, "name": "Private Endpoint Private DNS Zone Groups", "description": "This module deploys a Private Endpoint Private DNS Zone Group." @@ -51223,12 +51384,12 @@ "privateEndpoint": { "existing": true, "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2023-11-01", + "apiVersion": "2024-05-01", "name": "[parameters('privateEndpointName')]" }, "privateDnsZoneGroup": { "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2023-11-01", + "apiVersion": "2024-05-01", "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", "properties": { "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" @@ -51292,7 +51453,7 @@ "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('privateEndpoint', '2023-11-01', 'full').location]" + "value": "[reference('privateEndpoint', '2024-05-01', 'full').location]" }, "customDnsConfigs": { "type": "array", @@ -51346,7 +51507,7 @@ "value": "[last(split(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '/'))]" }, "secretsToSet": { - "value": "[union(createArray(), if(contains(parameters('secretsExportConfiguration'), 'accessKey1Name'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'accessKey1Name'), 'value', listKeys(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), '2023-05-01').key1)), createArray()), if(contains(parameters('secretsExportConfiguration'), 'accessKey2Name'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'accessKey2Name'), 'value', listKeys(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), '2023-05-01').key2)), createArray()))]" + "value": "[union(createArray(), if(contains(parameters('secretsExportConfiguration'), 'accessKey1Name'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'accessKey1Name'), 'value', listKeys('cognitiveService', '2025-04-01-preview').key1)), createArray()), if(contains(parameters('secretsExportConfiguration'), 'accessKey2Name'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'accessKey2Name'), 'value', listKeys('cognitiveService', '2025-04-01-preview').key2)), createArray()))]" } }, "template": { @@ -51356,8 +51517,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "1340925512665498953" + "version": "0.36.1.42791", + "templateHash": "1200612323329026557" } }, "definitions": { @@ -51386,7 +51547,7 @@ "metadata": { "description": "An AVM-aligned type for the output of the secret set via the secrets export feature.", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -51409,7 +51570,7 @@ "metadata": { "description": "An AVM-aligned type for the secret to set via the secrets export feature.", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } } @@ -51435,7 +51596,7 @@ "keyVault": { "existing": true, "type": "Microsoft.KeyVault/vaults", - "apiVersion": "2022-07-01", + "apiVersion": "2023-07-01", "name": "[parameters('keyVaultName')]" }, "secrets": { @@ -51507,7 +51668,7 @@ "value": "[reference('cognitiveService').endpoint]" }, "endpoints": { - "$ref": "#/definitions/endpointsType", + "$ref": "#/definitions/endpointType", "metadata": { "description": "All endpoints available for the cognitive services account, types depends on the cognitive service kind." }, @@ -51519,14 +51680,14 @@ "metadata": { "description": "The principal ID of the system assigned identity." }, - "value": "[tryGet(tryGet(reference('cognitiveService', '2023-05-01', 'full'), 'identity'), 'principalId')]" + "value": "[tryGet(tryGet(reference('cognitiveService', '2025-04-01-preview', 'full'), 'identity'), 'principalId')]" }, "location": { "type": "string", "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('cognitiveService', '2023-05-01', 'full').location]" + "value": "[reference('cognitiveService', '2025-04-01-preview', 'full').location]" }, "exportedSecrets": { "$ref": "#/definitions/secretsOutputType", @@ -51629,6 +51790,9 @@ "networkIsolation": { "value": "[parameters('networkIsolation')]" }, + "networkAcls": { + "value": "[parameters('networkAcls')]" + }, "virtualNetworkSubnetResourceId": "[if(parameters('networkIsolation'), createObject('value', parameters('virtualNetworkSubnetResourceId')), createObject('value', ''))]", "privateDnsZonesResourceIds": "[if(parameters('networkIsolation'), createObject('value', createArray(reference('cognitiveServicesPrivateDnsZone').outputs.resourceId.value)), createObject('value', createArray()))]", "logAnalyticsWorkspaceResourceId": { @@ -51646,7 +51810,7 @@ "_generator": { "name": "bicep", "version": "0.36.1.42791", - "templateHash": "16919614201922288466" + "templateHash": "2348080591288311162" } }, "definitions": { @@ -51951,6 +52115,12 @@ "metadata": { "description": "Optional. Tags to be applied to the resources." } + }, + "networkAcls": { + "type": "object", + "metadata": { + "description": "Optional. A collection of rules governing the accessibility from specific network locations." + } } }, "variables": { @@ -51991,6 +52161,9 @@ "kind": { "value": "[parameters('kind')]" }, + "allowProjectManagement": { + "value": true + }, "managedIdentities": { "value": { "systemAssigned": true @@ -52016,6 +52189,9 @@ "roleAssignments": { "value": "[parameters('roleAssignments')]" }, + "networkAcls": { + "value": "[parameters('networkAcls')]" + }, "privateEndpoints": "[if(parameters('networkIsolation'), createObject('value', createArray(createObject('privateDnsZoneGroup', createObject('privateDnsZoneGroupConfigs', variables('privateDnsZones')), 'subnetResourceId', parameters('virtualNetworkSubnetResourceId')))), createObject('value', createArray()))]" }, "template": { @@ -52025,8 +52201,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "616090829858030869" + "version": "0.36.1.42791", + "templateHash": "16135659971302525380" }, "name": "Cognitive Services", "description": "This module deploys a Cognitive Service." @@ -52092,112 +52268,110 @@ } }, "metadata": { - "__bicep_export!": true + "__bicep_export!": true, + "description": "The type for the private endpoint output." } }, - "deploymentsType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Specify the name of cognitive service account deployment." - } - }, - "model": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of Cognitive Services account deployment model." - } - }, - "format": { - "type": "string", - "metadata": { - "description": "Required. The format of Cognitive Services account deployment model." - } - }, - "version": { - "type": "string", - "metadata": { - "description": "Required. The version of Cognitive Services account deployment model." - } + "deploymentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of cognitive service account deployment." + } + }, + "model": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of Cognitive Services account deployment model." } }, - "metadata": { - "description": "Required. Properties of Cognitive Services account deployment model." - } - }, - "sku": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the resource model definition representing SKU." - } - }, - "capacity": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The capacity of the resource model definition representing SKU." - } - }, - "tier": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The tier of the resource model definition representing SKU." - } - }, - "size": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The size of the resource model definition representing SKU." - } - }, - "family": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The family of the resource model definition representing SKU." - } + "format": { + "type": "string", + "metadata": { + "description": "Required. The format of Cognitive Services account deployment model." } }, - "nullable": true, - "metadata": { - "description": "Optional. The resource model definition representing SKU." + "version": { + "type": "string", + "metadata": { + "description": "Required. The version of Cognitive Services account deployment model." + } } }, - "raiPolicyName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of RAI policy." + "metadata": { + "description": "Required. Properties of Cognitive Services account deployment model." + } + }, + "sku": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource model definition representing SKU." + } + }, + "capacity": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The capacity of the resource model definition representing SKU." + } + }, + "tier": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The tier of the resource model definition representing SKU." + } + }, + "size": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The size of the resource model definition representing SKU." + } + }, + "family": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The family of the resource model definition representing SKU." + } } }, - "versionUpgradeOption": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The version upgrade option." - } + "nullable": true, + "metadata": { + "description": "Optional. The resource model definition representing SKU." + } + }, + "raiPolicyName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of RAI policy." + } + }, + "versionUpgradeOption": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The version upgrade option." } } }, - "nullable": true, "metadata": { - "__bicep_export!": true + "__bicep_export!": true, + "description": "The type for a cognitive services account deployment." } }, - "endpointsType": { + "endpointType": { "type": "object", "properties": { "name": { @@ -52215,9 +52389,9 @@ } } }, - "nullable": true, "metadata": { - "__bicep_export!": true + "__bicep_export!": true, + "description": "The type for a cognitive services account endpoint." } }, "secretsExportConfigurationType": { @@ -52245,7 +52419,8 @@ } }, "metadata": { - "__bicep_export!": true + "__bicep_export!": true, + "description": "The type of the secrets exported to the provided Key Vault." } }, "_1.privateEndpointCustomDnsConfigType": { @@ -52270,7 +52445,7 @@ }, "metadata": { "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -52312,7 +52487,7 @@ }, "metadata": { "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -52353,7 +52528,7 @@ }, "metadata": { "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -52382,7 +52557,7 @@ "metadata": { "description": "An AVM-aligned type for the output of the secret set via the secrets export feature.", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -52419,7 +52594,7 @@ "metadata": { "description": "An AVM-aligned type for a customer-managed key. To be used if the resource type does not support auto-rotation of the customer-managed key.", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -52541,7 +52716,7 @@ "metadata": { "description": "An AVM-aligned type for a diagnostic setting. To be used if both logs & metrics are supported by the resource provider.", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -52571,7 +52746,7 @@ "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.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -52599,7 +52774,7 @@ "metadata": { "description": "An AVM-aligned type for a managed identity configuration. To be used if both a system-assigned & user-assigned identities are supported by the resource provider.", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -52640,6 +52815,13 @@ "description": "Required. Resource ID of the subnet where the endpoint needs to be created." } }, + "resourceGroupResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the Resource Group the Private Endpoint will be created in. If not specified, the Resource Group of the provided Virtual Network Subnet is used." + } + }, "privateDnsZoneGroup": { "$ref": "#/definitions/_1.privateEndpointPrivateDnsZoneGroupType", "nullable": true, @@ -52729,19 +52911,12 @@ "metadata": { "description": "Optional. Enable/Disable usage telemetry for module." } - }, - "resourceGroupName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Specify if you want to deploy the Private Endpoint into a different Resource Group than the main resource." - } } }, "metadata": { "description": "An AVM-aligned type for a private endpoint. To be used if the private endpoint's default service / groupId can be assumed (i.e., for services that only have one Private Endpoint type like 'vault' for key vault).", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -52816,7 +52991,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.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -52832,7 +53007,7 @@ "metadata": { "description": "A map of the exported secrets", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } } @@ -53056,7 +53231,11 @@ } }, "deployments": { - "$ref": "#/definitions/deploymentsType", + "type": "array", + "items": { + "$ref": "#/definitions/deploymentType" + }, + "nullable": true, "metadata": { "description": "Optional. Array of deployments about cognitive service accounts to create." } @@ -53067,6 +53246,13 @@ "metadata": { "description": "Optional. Key vault reference and secret settings for the module's secrets export." } + }, + "allowProjectManagement": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable/Disable project management feature for AI Foundry." + } } }, "variables": { @@ -53105,6 +53291,7 @@ "Cognitive Services Speech Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0e75ca1e-0464-4b4d-8b93-68208a576181')]", "Cognitive Services Speech User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f2dc8367-1007-4938-bd23-fe263f013447')]", "Cognitive Services User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a97b65f3-24c7-4388-baec-2e87135dc908')]", + "Azure AI Developer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '64702f94-c441-49e6-a78b-ef80e0188fee')]", "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", @@ -53117,7 +53304,7 @@ "condition": "[and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), not(empty(tryGet(parameters('customerManagedKey'), 'keyName')))))]", "existing": true, "type": "Microsoft.KeyVault/vaults/keys", - "apiVersion": "2023-02-01", + "apiVersion": "2023-07-01", "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[2]]", "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[4]]", "name": "[format('{0}/{1}', last(split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')), tryGet(parameters('customerManagedKey'), 'keyName'))]" @@ -53126,7 +53313,7 @@ "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2024-03-01", - "name": "[format('46d3xbcp.res.cognitiveservices-account.{0}.{1}', replace('0.10.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "name": "[format('46d3xbcp.res.cognitiveservices-account.{0}.{1}', replace('0.11.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -53146,7 +53333,7 @@ "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId')))]", "existing": true, "type": "Microsoft.KeyVault/vaults", - "apiVersion": "2023-02-01", + "apiVersion": "2023-07-01", "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[2]]", "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[4]]", "name": "[last(split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/'))]" @@ -53155,14 +53342,14 @@ "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId')))]", "existing": true, "type": "Microsoft.ManagedIdentity/userAssignedIdentities", - "apiVersion": "2023-01-31", + "apiVersion": "2025-01-31-preview", "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/')[2]]", "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/')[4]]", "name": "[last(split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/'))]" }, "cognitiveService": { "type": "Microsoft.CognitiveServices/accounts", - "apiVersion": "2023-05-01", + "apiVersion": "2025-04-01-preview", "name": "[parameters('name')]", "kind": "[parameters('kind')]", "identity": "[variables('identity')]", @@ -53172,6 +53359,7 @@ "name": "[parameters('sku')]" }, "properties": { + "allowProjectManagement": "[parameters('allowProjectManagement')]", "customSubDomainName": "[parameters('customSubDomainName')]", "networkAcls": "[if(not(empty(coalesce(parameters('networkAcls'), createObject()))), createObject('defaultAction', tryGet(parameters('networkAcls'), 'defaultAction'), 'virtualNetworkRules', coalesce(tryGet(parameters('networkAcls'), 'virtualNetworkRules'), createArray()), 'ipRules', coalesce(tryGet(parameters('networkAcls'), 'ipRules'), createArray())), null())]", "publicNetworkAccess": "[if(not(equals(parameters('publicNetworkAccess'), null())), parameters('publicNetworkAccess'), if(not(empty(parameters('networkAcls'))), 'Enabled', 'Disabled'))]", @@ -53186,8 +53374,8 @@ "dynamicThrottlingEnabled": "[parameters('dynamicThrottlingEnabled')]" }, "dependsOn": [ - "cMKKeyVault::cMKKey", "cMKKeyVault", + "cMKKeyVault::cMKKey", "cMKUserAssignedIdentity" ] }, @@ -53199,7 +53387,7 @@ "batchSize": 1 }, "type": "Microsoft.CognitiveServices/accounts/deployments", - "apiVersion": "2023-05-01", + "apiVersion": "2025-04-01-preview", "name": "[format('{0}/{1}', parameters('name'), coalesce(tryGet(coalesce(parameters('deployments'), createArray())[copyIndex()], 'name'), format('{0}-deployments', parameters('name'))))]", "properties": { "model": "[coalesce(parameters('deployments'), createArray())[copyIndex()].model]", @@ -53350,8 +53538,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "15954548978129725136" + "version": "0.34.44.8038", + "templateHash": "12389807800450456797" }, "name": "Private Endpoints", "description": "This module deploys a Private Endpoint." @@ -53760,7 +53948,7 @@ "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2024-03-01", - "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.10.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.11.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -53778,7 +53966,7 @@ }, "privateEndpoint": { "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2023-11-01", + "apiVersion": "2024-05-01", "name": "[parameters('name')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", @@ -53866,8 +54054,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "5440815542537978381" + "version": "0.34.44.8038", + "templateHash": "13997305779829540948" }, "name": "Private Endpoint Private DNS Zone Groups", "description": "This module deploys a Private Endpoint Private DNS Zone Group." @@ -53939,12 +54127,12 @@ "privateEndpoint": { "existing": true, "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2023-11-01", + "apiVersion": "2024-05-01", "name": "[parameters('privateEndpointName')]" }, "privateDnsZoneGroup": { "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2023-11-01", + "apiVersion": "2024-05-01", "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", "properties": { "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" @@ -54008,7 +54196,7 @@ "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('privateEndpoint', '2023-11-01', 'full').location]" + "value": "[reference('privateEndpoint', '2024-05-01', 'full').location]" }, "customDnsConfigs": { "type": "array", @@ -54062,7 +54250,7 @@ "value": "[last(split(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '/'))]" }, "secretsToSet": { - "value": "[union(createArray(), if(contains(parameters('secretsExportConfiguration'), 'accessKey1Name'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'accessKey1Name'), 'value', listKeys(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), '2023-05-01').key1)), createArray()), if(contains(parameters('secretsExportConfiguration'), 'accessKey2Name'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'accessKey2Name'), 'value', listKeys(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), '2023-05-01').key2)), createArray()))]" + "value": "[union(createArray(), if(contains(parameters('secretsExportConfiguration'), 'accessKey1Name'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'accessKey1Name'), 'value', listKeys('cognitiveService', '2025-04-01-preview').key1)), createArray()), if(contains(parameters('secretsExportConfiguration'), 'accessKey2Name'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'accessKey2Name'), 'value', listKeys('cognitiveService', '2025-04-01-preview').key2)), createArray()))]" } }, "template": { @@ -54072,8 +54260,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "1340925512665498953" + "version": "0.36.1.42791", + "templateHash": "1200612323329026557" } }, "definitions": { @@ -54102,7 +54290,7 @@ "metadata": { "description": "An AVM-aligned type for the output of the secret set via the secrets export feature.", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -54125,7 +54313,7 @@ "metadata": { "description": "An AVM-aligned type for the secret to set via the secrets export feature.", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } } @@ -54151,7 +54339,7 @@ "keyVault": { "existing": true, "type": "Microsoft.KeyVault/vaults", - "apiVersion": "2022-07-01", + "apiVersion": "2023-07-01", "name": "[parameters('keyVaultName')]" }, "secrets": { @@ -54223,7 +54411,7 @@ "value": "[reference('cognitiveService').endpoint]" }, "endpoints": { - "$ref": "#/definitions/endpointsType", + "$ref": "#/definitions/endpointType", "metadata": { "description": "All endpoints available for the cognitive services account, types depends on the cognitive service kind." }, @@ -54235,14 +54423,14 @@ "metadata": { "description": "The principal ID of the system assigned identity." }, - "value": "[tryGet(tryGet(reference('cognitiveService', '2023-05-01', 'full'), 'identity'), 'principalId')]" + "value": "[tryGet(tryGet(reference('cognitiveService', '2025-04-01-preview', 'full'), 'identity'), 'principalId')]" }, "location": { "type": "string", "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('cognitiveService', '2023-05-01', 'full').location]" + "value": "[reference('cognitiveService', '2025-04-01-preview', 'full').location]" }, "exportedSecrets": { "$ref": "#/definitions/secretsOutputType", @@ -54347,6 +54535,9 @@ "logAnalyticsWorkspaceResourceId": { "value": "[parameters('logAnalyticsWorkspaceResourceId')]" }, + "networkAcls": { + "value": "[parameters('networkAcls')]" + }, "tags": { "value": "[parameters('tags')]" } @@ -54359,7 +54550,7 @@ "_generator": { "name": "bicep", "version": "0.36.1.42791", - "templateHash": "16919614201922288466" + "templateHash": "2348080591288311162" } }, "definitions": { @@ -54664,6 +54855,12 @@ "metadata": { "description": "Optional. Tags to be applied to the resources." } + }, + "networkAcls": { + "type": "object", + "metadata": { + "description": "Optional. A collection of rules governing the accessibility from specific network locations." + } } }, "variables": { @@ -54704,6 +54901,9 @@ "kind": { "value": "[parameters('kind')]" }, + "allowProjectManagement": { + "value": true + }, "managedIdentities": { "value": { "systemAssigned": true @@ -54729,6 +54929,9 @@ "roleAssignments": { "value": "[parameters('roleAssignments')]" }, + "networkAcls": { + "value": "[parameters('networkAcls')]" + }, "privateEndpoints": "[if(parameters('networkIsolation'), createObject('value', createArray(createObject('privateDnsZoneGroup', createObject('privateDnsZoneGroupConfigs', variables('privateDnsZones')), 'subnetResourceId', parameters('virtualNetworkSubnetResourceId')))), createObject('value', createArray()))]" }, "template": { @@ -54738,8 +54941,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "616090829858030869" + "version": "0.36.1.42791", + "templateHash": "16135659971302525380" }, "name": "Cognitive Services", "description": "This module deploys a Cognitive Service." @@ -54805,112 +55008,110 @@ } }, "metadata": { - "__bicep_export!": true + "__bicep_export!": true, + "description": "The type for the private endpoint output." } }, - "deploymentsType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Specify the name of cognitive service account deployment." - } - }, - "model": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of Cognitive Services account deployment model." - } - }, - "format": { - "type": "string", - "metadata": { - "description": "Required. The format of Cognitive Services account deployment model." - } - }, - "version": { - "type": "string", - "metadata": { - "description": "Required. The version of Cognitive Services account deployment model." - } + "deploymentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of cognitive service account deployment." + } + }, + "model": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of Cognitive Services account deployment model." } }, - "metadata": { - "description": "Required. Properties of Cognitive Services account deployment model." - } - }, - "sku": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the resource model definition representing SKU." - } - }, - "capacity": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The capacity of the resource model definition representing SKU." - } - }, - "tier": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The tier of the resource model definition representing SKU." - } - }, - "size": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The size of the resource model definition representing SKU." - } - }, - "family": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The family of the resource model definition representing SKU." - } + "format": { + "type": "string", + "metadata": { + "description": "Required. The format of Cognitive Services account deployment model." } }, - "nullable": true, - "metadata": { - "description": "Optional. The resource model definition representing SKU." + "version": { + "type": "string", + "metadata": { + "description": "Required. The version of Cognitive Services account deployment model." + } } }, - "raiPolicyName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of RAI policy." + "metadata": { + "description": "Required. Properties of Cognitive Services account deployment model." + } + }, + "sku": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource model definition representing SKU." + } + }, + "capacity": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The capacity of the resource model definition representing SKU." + } + }, + "tier": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The tier of the resource model definition representing SKU." + } + }, + "size": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The size of the resource model definition representing SKU." + } + }, + "family": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The family of the resource model definition representing SKU." + } } }, - "versionUpgradeOption": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The version upgrade option." - } + "nullable": true, + "metadata": { + "description": "Optional. The resource model definition representing SKU." + } + }, + "raiPolicyName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of RAI policy." + } + }, + "versionUpgradeOption": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The version upgrade option." } } }, - "nullable": true, "metadata": { - "__bicep_export!": true + "__bicep_export!": true, + "description": "The type for a cognitive services account deployment." } }, - "endpointsType": { + "endpointType": { "type": "object", "properties": { "name": { @@ -54928,9 +55129,9 @@ } } }, - "nullable": true, "metadata": { - "__bicep_export!": true + "__bicep_export!": true, + "description": "The type for a cognitive services account endpoint." } }, "secretsExportConfigurationType": { @@ -54958,7 +55159,8 @@ } }, "metadata": { - "__bicep_export!": true + "__bicep_export!": true, + "description": "The type of the secrets exported to the provided Key Vault." } }, "_1.privateEndpointCustomDnsConfigType": { @@ -54983,7 +55185,7 @@ }, "metadata": { "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -55025,7 +55227,7 @@ }, "metadata": { "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -55066,7 +55268,7 @@ }, "metadata": { "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -55095,7 +55297,7 @@ "metadata": { "description": "An AVM-aligned type for the output of the secret set via the secrets export feature.", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -55132,7 +55334,7 @@ "metadata": { "description": "An AVM-aligned type for a customer-managed key. To be used if the resource type does not support auto-rotation of the customer-managed key.", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -55254,7 +55456,7 @@ "metadata": { "description": "An AVM-aligned type for a diagnostic setting. To be used if both logs & metrics are supported by the resource provider.", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -55284,7 +55486,7 @@ "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.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -55312,7 +55514,7 @@ "metadata": { "description": "An AVM-aligned type for a managed identity configuration. To be used if both a system-assigned & user-assigned identities are supported by the resource provider.", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -55353,6 +55555,13 @@ "description": "Required. Resource ID of the subnet where the endpoint needs to be created." } }, + "resourceGroupResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the Resource Group the Private Endpoint will be created in. If not specified, the Resource Group of the provided Virtual Network Subnet is used." + } + }, "privateDnsZoneGroup": { "$ref": "#/definitions/_1.privateEndpointPrivateDnsZoneGroupType", "nullable": true, @@ -55442,19 +55651,12 @@ "metadata": { "description": "Optional. Enable/Disable usage telemetry for module." } - }, - "resourceGroupName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Specify if you want to deploy the Private Endpoint into a different Resource Group than the main resource." - } } }, "metadata": { "description": "An AVM-aligned type for a private endpoint. To be used if the private endpoint's default service / groupId can be assumed (i.e., for services that only have one Private Endpoint type like 'vault' for key vault).", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -55529,7 +55731,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.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -55545,7 +55747,7 @@ "metadata": { "description": "A map of the exported secrets", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } } @@ -55769,7 +55971,11 @@ } }, "deployments": { - "$ref": "#/definitions/deploymentsType", + "type": "array", + "items": { + "$ref": "#/definitions/deploymentType" + }, + "nullable": true, "metadata": { "description": "Optional. Array of deployments about cognitive service accounts to create." } @@ -55780,6 +55986,13 @@ "metadata": { "description": "Optional. Key vault reference and secret settings for the module's secrets export." } + }, + "allowProjectManagement": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable/Disable project management feature for AI Foundry." + } } }, "variables": { @@ -55818,6 +56031,7 @@ "Cognitive Services Speech Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0e75ca1e-0464-4b4d-8b93-68208a576181')]", "Cognitive Services Speech User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f2dc8367-1007-4938-bd23-fe263f013447')]", "Cognitive Services User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a97b65f3-24c7-4388-baec-2e87135dc908')]", + "Azure AI Developer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '64702f94-c441-49e6-a78b-ef80e0188fee')]", "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", @@ -55830,7 +56044,7 @@ "condition": "[and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), not(empty(tryGet(parameters('customerManagedKey'), 'keyName')))))]", "existing": true, "type": "Microsoft.KeyVault/vaults/keys", - "apiVersion": "2023-02-01", + "apiVersion": "2023-07-01", "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[2]]", "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[4]]", "name": "[format('{0}/{1}', last(split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')), tryGet(parameters('customerManagedKey'), 'keyName'))]" @@ -55839,7 +56053,7 @@ "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2024-03-01", - "name": "[format('46d3xbcp.res.cognitiveservices-account.{0}.{1}', replace('0.10.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "name": "[format('46d3xbcp.res.cognitiveservices-account.{0}.{1}', replace('0.11.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -55859,7 +56073,7 @@ "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId')))]", "existing": true, "type": "Microsoft.KeyVault/vaults", - "apiVersion": "2023-02-01", + "apiVersion": "2023-07-01", "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[2]]", "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[4]]", "name": "[last(split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/'))]" @@ -55868,14 +56082,14 @@ "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId')))]", "existing": true, "type": "Microsoft.ManagedIdentity/userAssignedIdentities", - "apiVersion": "2023-01-31", + "apiVersion": "2025-01-31-preview", "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/')[2]]", "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/')[4]]", "name": "[last(split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/'))]" }, "cognitiveService": { "type": "Microsoft.CognitiveServices/accounts", - "apiVersion": "2023-05-01", + "apiVersion": "2025-04-01-preview", "name": "[parameters('name')]", "kind": "[parameters('kind')]", "identity": "[variables('identity')]", @@ -55885,6 +56099,7 @@ "name": "[parameters('sku')]" }, "properties": { + "allowProjectManagement": "[parameters('allowProjectManagement')]", "customSubDomainName": "[parameters('customSubDomainName')]", "networkAcls": "[if(not(empty(coalesce(parameters('networkAcls'), createObject()))), createObject('defaultAction', tryGet(parameters('networkAcls'), 'defaultAction'), 'virtualNetworkRules', coalesce(tryGet(parameters('networkAcls'), 'virtualNetworkRules'), createArray()), 'ipRules', coalesce(tryGet(parameters('networkAcls'), 'ipRules'), createArray())), null())]", "publicNetworkAccess": "[if(not(equals(parameters('publicNetworkAccess'), null())), parameters('publicNetworkAccess'), if(not(empty(parameters('networkAcls'))), 'Enabled', 'Disabled'))]", @@ -55899,8 +56114,8 @@ "dynamicThrottlingEnabled": "[parameters('dynamicThrottlingEnabled')]" }, "dependsOn": [ - "cMKKeyVault::cMKKey", "cMKKeyVault", + "cMKKeyVault::cMKKey", "cMKUserAssignedIdentity" ] }, @@ -55912,7 +56127,7 @@ "batchSize": 1 }, "type": "Microsoft.CognitiveServices/accounts/deployments", - "apiVersion": "2023-05-01", + "apiVersion": "2025-04-01-preview", "name": "[format('{0}/{1}', parameters('name'), coalesce(tryGet(coalesce(parameters('deployments'), createArray())[copyIndex()], 'name'), format('{0}-deployments', parameters('name'))))]", "properties": { "model": "[coalesce(parameters('deployments'), createArray())[copyIndex()].model]", @@ -56063,8 +56278,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "15954548978129725136" + "version": "0.34.44.8038", + "templateHash": "12389807800450456797" }, "name": "Private Endpoints", "description": "This module deploys a Private Endpoint." @@ -56473,7 +56688,7 @@ "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2024-03-01", - "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.10.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.11.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -56491,7 +56706,7 @@ }, "privateEndpoint": { "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2023-11-01", + "apiVersion": "2024-05-01", "name": "[parameters('name')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", @@ -56579,8 +56794,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "5440815542537978381" + "version": "0.34.44.8038", + "templateHash": "13997305779829540948" }, "name": "Private Endpoint Private DNS Zone Groups", "description": "This module deploys a Private Endpoint Private DNS Zone Group." @@ -56652,12 +56867,12 @@ "privateEndpoint": { "existing": true, "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2023-11-01", + "apiVersion": "2024-05-01", "name": "[parameters('privateEndpointName')]" }, "privateDnsZoneGroup": { "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2023-11-01", + "apiVersion": "2024-05-01", "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", "properties": { "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" @@ -56721,7 +56936,7 @@ "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('privateEndpoint', '2023-11-01', 'full').location]" + "value": "[reference('privateEndpoint', '2024-05-01', 'full').location]" }, "customDnsConfigs": { "type": "array", @@ -56775,7 +56990,7 @@ "value": "[last(split(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '/'))]" }, "secretsToSet": { - "value": "[union(createArray(), if(contains(parameters('secretsExportConfiguration'), 'accessKey1Name'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'accessKey1Name'), 'value', listKeys(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), '2023-05-01').key1)), createArray()), if(contains(parameters('secretsExportConfiguration'), 'accessKey2Name'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'accessKey2Name'), 'value', listKeys(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), '2023-05-01').key2)), createArray()))]" + "value": "[union(createArray(), if(contains(parameters('secretsExportConfiguration'), 'accessKey1Name'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'accessKey1Name'), 'value', listKeys('cognitiveService', '2025-04-01-preview').key1)), createArray()), if(contains(parameters('secretsExportConfiguration'), 'accessKey2Name'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'accessKey2Name'), 'value', listKeys('cognitiveService', '2025-04-01-preview').key2)), createArray()))]" } }, "template": { @@ -56785,8 +57000,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "1340925512665498953" + "version": "0.36.1.42791", + "templateHash": "1200612323329026557" } }, "definitions": { @@ -56815,7 +57030,7 @@ "metadata": { "description": "An AVM-aligned type for the output of the secret set via the secrets export feature.", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -56838,7 +57053,7 @@ "metadata": { "description": "An AVM-aligned type for the secret to set via the secrets export feature.", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } } @@ -56864,7 +57079,7 @@ "keyVault": { "existing": true, "type": "Microsoft.KeyVault/vaults", - "apiVersion": "2022-07-01", + "apiVersion": "2023-07-01", "name": "[parameters('keyVaultName')]" }, "secrets": { @@ -56936,7 +57151,7 @@ "value": "[reference('cognitiveService').endpoint]" }, "endpoints": { - "$ref": "#/definitions/endpointsType", + "$ref": "#/definitions/endpointType", "metadata": { "description": "All endpoints available for the cognitive services account, types depends on the cognitive service kind." }, @@ -56948,14 +57163,14 @@ "metadata": { "description": "The principal ID of the system assigned identity." }, - "value": "[tryGet(tryGet(reference('cognitiveService', '2023-05-01', 'full'), 'identity'), 'principalId')]" + "value": "[tryGet(tryGet(reference('cognitiveService', '2025-04-01-preview', 'full'), 'identity'), 'principalId')]" }, "location": { "type": "string", "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('cognitiveService', '2023-05-01', 'full').location]" + "value": "[reference('cognitiveService', '2025-04-01-preview', 'full').location]" }, "exportedSecrets": { "$ref": "#/definitions/secretsOutputType", @@ -57062,6 +57277,207 @@ "network" ] }, + "project": { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}prj', parameters('name'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "cosmosDBname": "[if(parameters('cosmosDbEnabled'), createObject('value', reference('cosmosDb').outputs.cosmosDBname.value), createObject('value', ''))]", + "cosmosDbEnabled": { + "value": "[parameters('cosmosDbEnabled')]" + }, + "searchEnabled": { + "value": "[parameters('searchEnabled')]" + }, + "name": { + "value": "[parameters('projectName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "storageName": { + "value": "[reference('storageAccount').outputs.storageName.value]" + }, + "aiServicesName": { + "value": "[reference('cognitiveServices').outputs.aiServicesName.value]" + }, + "nameFormatted": "[if(parameters('searchEnabled'), createObject('value', reference('aiSearch').outputs.name.value), createObject('value', ''))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.36.1.42791", + "templateHash": "15578119539506362308" + } + }, + "parameters": { + "name": { + "type": "string", + "minLength": 3, + "maxLength": 12, + "metadata": { + "description": "The name of the environment. Use alphanumeric characters only." + } + }, + "location": { + "type": "string", + "metadata": { + "description": "Specifies the location for all the Azure resources. Defaults to the location of the resource group." + } + }, + "cosmosDBname": { + "type": "string", + "metadata": { + "description": "Name of the customers existing CosmosDB Resource" + } + }, + "cosmosDbEnabled": { + "type": "bool", + "metadata": { + "description": "Whether to include Cosmos DB in the deployment." + } + }, + "storageName": { + "type": "string", + "metadata": { + "description": "Name of the customers existing Azure Storage Account" + } + }, + "aiServicesName": { + "type": "string", + "metadata": { + "description": "Foundry Account Name" + } + }, + "searchEnabled": { + "type": "bool", + "metadata": { + "description": "Whether to include Azure AI Search in the deployment." + } + }, + "nameFormatted": { + "type": "string", + "metadata": { + "description": "Azure Search Service Name" + } + }, + "defaultProjectName": { + "type": "string", + "defaultValue": "[parameters('name')]", + "metadata": { + "description": "Name of the first project" + } + }, + "defaultProjectDisplayName": { + "type": "string", + "defaultValue": "[parameters('name')]" + }, + "defaultProjectDescription": { + "type": "string", + "defaultValue": "This is a sample project for AI Foundry." + } + }, + "resources": [ + { + "type": "Microsoft.CognitiveServices/accounts/projects", + "apiVersion": "2025-04-01-preview", + "name": "[format('{0}/{1}', parameters('aiServicesName'), parameters('defaultProjectName'))]", + "location": "[parameters('location')]", + "identity": { + "type": "SystemAssigned" + }, + "properties": { + "displayName": "[parameters('defaultProjectDisplayName')]", + "description": "[parameters('defaultProjectDescription')]" + } + }, + { + "type": "Microsoft.CognitiveServices/accounts/projects/connections", + "apiVersion": "2025-04-01-preview", + "name": "[format('{0}/{1}/{2}', parameters('aiServicesName'), parameters('defaultProjectName'), parameters('storageName'))]", + "properties": { + "category": "AzureBlob", + "target": "[reference(resourceId('Microsoft.Storage/storageAccounts', parameters('storageName')), '2023-01-01').primaryEndpoints.blob]", + "authType": "AAD", + "metadata": { + "ApiType": "Azure", + "ResourceId": "[resourceId('Microsoft.Storage/storageAccounts', parameters('storageName'))]", + "location": "[reference(resourceId('Microsoft.Storage/storageAccounts', parameters('storageName')), '2023-01-01', 'full').location]", + "accountName": "[parameters('storageName')]", + "containerName": "[format('{0}proj', parameters('name'))]" + } + }, + "dependsOn": [ + "[resourceId('Microsoft.CognitiveServices/accounts/projects', parameters('aiServicesName'), parameters('defaultProjectName'))]" + ] + }, + { + "condition": "[parameters('searchEnabled')]", + "type": "Microsoft.CognitiveServices/accounts/projects/connections", + "apiVersion": "2025-04-01-preview", + "name": "[format('{0}/{1}/{2}', parameters('aiServicesName'), parameters('defaultProjectName'), if(parameters('searchEnabled'), parameters('nameFormatted'), ''))]", + "properties": { + "category": "CognitiveSearch", + "target": "[if(parameters('searchEnabled'), format('https://{0}.search.windows.net/', parameters('nameFormatted')), '')]", + "authType": "AAD", + "isSharedToAll": true, + "metadata": { + "ApiType": "Azure", + "ResourceId": "[if(parameters('searchEnabled'), resourceId('Microsoft.Search/searchServices', parameters('nameFormatted')), '')]", + "location": "[if(parameters('searchEnabled'), reference(resourceId('Microsoft.Search/searchServices', parameters('nameFormatted')), '2024-06-01-preview', 'full').location, '')]" + } + }, + "dependsOn": [ + "[resourceId('Microsoft.CognitiveServices/accounts/projects', parameters('aiServicesName'), parameters('defaultProjectName'))]" + ] + }, + { + "condition": "[parameters('cosmosDbEnabled')]", + "type": "Microsoft.CognitiveServices/accounts/projects/connections", + "apiVersion": "2025-04-01-preview", + "name": "[format('{0}/{1}/{2}', parameters('aiServicesName'), parameters('defaultProjectName'), parameters('cosmosDBname'))]", + "properties": { + "category": "CosmosDB", + "target": "[if(parameters('cosmosDbEnabled'), reference(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('cosmosDBname')), '2025-05-01-preview').documentEndpoint, '')]", + "authType": "AAD", + "metadata": { + "ApiType": "Azure", + "ResourceId": "[if(parameters('cosmosDbEnabled'), resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('cosmosDBname')), '')]", + "location": "[if(parameters('cosmosDbEnabled'), reference(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('cosmosDBname')), '2025-05-01-preview', 'full').location, '')]" + } + }, + "dependsOn": [ + "[resourceId('Microsoft.CognitiveServices/accounts/projects', parameters('aiServicesName'), parameters('defaultProjectName'))]" + ] + } + ], + "outputs": { + "projectId": { + "type": "string", + "value": "[resourceId('Microsoft.CognitiveServices/accounts/projects', parameters('aiServicesName'), parameters('defaultProjectName'))]" + }, + "projectName": { + "type": "string", + "value": "[parameters('defaultProjectName')]" + } + } + } + }, + "dependsOn": [ + "aiSearch", + "cognitiveServices", + "cosmosDb", + "storageAccount" + ] + }, "aiSearch": { "condition": "[parameters('searchEnabled')]", "type": "Microsoft.Resources/deployments", @@ -57087,6 +57503,9 @@ "logAnalyticsWorkspaceResourceId": { "value": "[reference('logAnalyticsWorkspace').outputs.resourceId.value]" }, + "userObjectId": { + "value": "[parameters('userObjectId')]" + }, "roleAssignments": { "value": "[union(if(empty(parameters('userObjectId')), createArray(), createArray(createObject('principalId', parameters('userObjectId'), 'principalType', 'User', 'roleDefinitionIdOrName', 'Search Index Data Contributor'))), createArray(createObject('principalId', reference('cognitiveServices').outputs.aiServicesSystemAssignedMIPrincipalId.value, 'principalType', 'ServicePrincipal', 'roleDefinitionIdOrName', 'Search Index Data Contributor'), createObject('principalId', reference('cognitiveServices').outputs.aiServicesSystemAssignedMIPrincipalId.value, 'principalType', 'ServicePrincipal', 'roleDefinitionIdOrName', 'Search Service Contributor')))]" }, @@ -57102,7 +57521,7 @@ "_generator": { "name": "bicep", "version": "0.36.1.42791", - "templateHash": "2345622020738803913" + "templateHash": "10624928188153796868" } }, "definitions": { @@ -57227,6 +57646,12 @@ "description": "Specifies whether network isolation is enabled. This will create a private endpoint for the AI Search resource and link the private DNS zone." } }, + "userObjectId": { + "type": "string", + "metadata": { + "description": "Specifies the object id of a Microsoft Entra ID user. In general, this the object id of the system administrator who deploys the Azure resources. This defaults to the deploying user." + } + }, "roleAssignments": { "type": "array", "items": { @@ -60302,7 +60727,7 @@ "value": "[parameters('location')]" }, "cmkEnforcement": { - "value": "Enabled" + "value": "Disabled" }, "managedIdentities": { "value": { @@ -60322,9 +60747,7 @@ "replicaCount": { "value": 3 }, - "roleAssignments": { - "value": "[parameters('roleAssignments')]" - }, + "roleAssignments": "[if(empty(parameters('userObjectId')), createObject('value', createArray()), createObject('value', createArray(createObject('principalId', parameters('userObjectId'), 'principalType', 'User', 'roleDefinitionIdOrName', 'Search Index Data Contributor'), createObject('principalId', parameters('userObjectId'), 'principalType', 'User', 'roleDefinitionIdOrName', 'Search Index Data Reader'))))]", "diagnosticSettings": { "value": [ { @@ -62456,7 +62879,7 @@ "value": "[reference('network').outputs.vmSubnetId.value]" }, "storageAccountName": { - "value": "[reference('storageAccount').outputs.name.value]" + "value": "[reference('storageAccount').outputs.storageName.value]" }, "storageAccountResourceGroup": { "value": "[resourceGroup().name]" @@ -62557,15620 +62980,491 @@ } }, "imagePublisher": { - "type": "string", - "defaultValue": "MicrosoftWindowsServer", - "metadata": { - "description": "Specifies the image publisher of the disk image used to create the virtual machine." - } - }, - "imageOffer": { - "type": "string", - "defaultValue": "WindowsServer", - "metadata": { - "description": "Specifies the offer of the platform image or marketplace image used to create the virtual machine." - } - }, - "imageSku": { - "type": "string", - "defaultValue": "2022-datacenter-azure-edition", - "metadata": { - "description": "Specifies the image version for the virtual machine." - } - }, - "authenticationType": { - "type": "string", - "defaultValue": "password", - "allowedValues": [ - "sshPublicKey", - "password" - ], - "metadata": { - "description": "Specifies the type of authentication when accessing the Virtual Machine. SSH key is recommended." - } - }, - "vmAdminUsername": { - "type": "string", - "metadata": { - "description": "Specifies the name of the administrator account of the virtual machine." - } - }, - "vmAdminPasswordOrKey": { - "type": "securestring", - "metadata": { - "description": "Specifies the SSH Key or password for the virtual machine. SSH key is recommended." - } - }, - "diskStorageAccountType": { - "type": "string", - "defaultValue": "Premium_LRS", - "allowedValues": [ - "Premium_LRS", - "StandardSSD_LRS", - "Standard_LRS", - "UltraSSD_LRS" - ], - "metadata": { - "description": "Specifies the storage account type for OS and data disk." - } - }, - "numDataDisks": { - "type": "int", - "defaultValue": 1, - "minValue": 0, - "maxValue": 64, - "metadata": { - "description": "Specifies the number of data disks of the virtual machine." - } - }, - "osDiskSize": { - "type": "int", - "defaultValue": 128, - "metadata": { - "description": "Specifies the size in GB of the OS disk of the VM." - } - }, - "dataDiskSize": { - "type": "int", - "defaultValue": 50, - "metadata": { - "description": "Specifies the size in GB of the OS disk of the virtual machine." - } - }, - "dataDiskCaching": { - "type": "string", - "defaultValue": "ReadWrite", - "metadata": { - "description": "Specifies the caching requirements for the data disks." - } - }, - "enableMicrosoftEntraIdAuth": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Specifies whether enabling Microsoft Entra ID authentication on the virtual machine." - } - }, - "enableAcceleratedNetworking": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Specifies whether enabling accelerated networking on the virtual machine." - } - }, - "vmNicName": { - "type": "string", - "metadata": { - "description": "Specifies the name of the network interface of the virtual machine." - } - }, - "userObjectId": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Specifies the object id of a Microsoft Entra ID user. In general, this the object id of the system administrator who deploys the Azure resources." - } - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]", - "metadata": { - "description": "Specifies the location." - } - }, - "workspaceId": { - "type": "string", - "metadata": { - "description": "Specifies the resource id of the Log Analytics workspace." - } - }, - "tags": { - "type": "object", - "metadata": { - "description": "Specifies the resource tags." - } - } - }, - "variables": { - "randomString": "[uniqueString(resourceGroup().id, parameters('vmName'), parameters('vmAdminPasswordOrKey'))]", - "adminPassword": "[if(less(length(parameters('vmAdminPasswordOrKey')), 8), format('{0}{1}', parameters('vmAdminPasswordOrKey'), take(variables('randomString'), 12)), parameters('vmAdminPasswordOrKey'))]", - "linuxConfiguration": { - "disablePasswordAuthentication": true, - "ssh": { - "publicKeys": [ - { - "path": "[format('/home/{0}/.ssh/authorized_keys', parameters('vmAdminUsername'))]", - "keyData": "[variables('adminPassword')]" - } - ] - }, - "provisionVMAgent": true - } - }, - "resources": [ - { - "type": "Microsoft.Network/networkInterfaces", - "apiVersion": "2021-08-01", - "name": "[parameters('vmNicName')]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", - "properties": { - "enableAcceleratedNetworking": "[parameters('enableAcceleratedNetworking')]", - "ipConfigurations": [ - { - "name": "ipconfig1", - "properties": { - "privateIPAllocationMethod": "Dynamic", - "subnet": { - "id": "[parameters('vmSubnetId')]" - } - } - } - ] - } - }, - { - "type": "Microsoft.Compute/virtualMachines", - "apiVersion": "2021-11-01", - "name": "[parameters('vmName')]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", - "properties": { - "hardwareProfile": { - "vmSize": "[parameters('vmSize')]" - }, - "osProfile": { - "computerName": "[take(parameters('vmName'), 15)]", - "adminUsername": "[parameters('vmAdminUsername')]", - "adminPassword": "[variables('adminPassword')]", - "linuxConfiguration": "[if(equals(parameters('authenticationType'), 'password'), null(), variables('linuxConfiguration'))]" - }, - "storageProfile": { - "copy": [ - { - "name": "dataDisks", - "count": "[length(range(0, parameters('numDataDisks')))]", - "input": { - "caching": "[parameters('dataDiskCaching')]", - "diskSizeGB": "[parameters('dataDiskSize')]", - "lun": "[range(0, parameters('numDataDisks'))[copyIndex('dataDisks')]]", - "name": "[format('{0}-DataDisk{1}', parameters('vmName'), range(0, parameters('numDataDisks'))[copyIndex('dataDisks')])]", - "createOption": "Empty", - "managedDisk": { - "storageAccountType": "[parameters('diskStorageAccountType')]" - } - } - } - ], - "imageReference": { - "publisher": "[parameters('imagePublisher')]", - "offer": "[parameters('imageOffer')]", - "sku": "[parameters('imageSku')]", - "version": "latest" - }, - "osDisk": { - "name": "[format('{0}_OSDisk', parameters('vmName'))]", - "caching": "ReadWrite", - "createOption": "FromImage", - "diskSizeGB": "[parameters('osDiskSize')]", - "managedDisk": { - "storageAccountType": "[parameters('diskStorageAccountType')]" - } - } - }, - "networkProfile": { - "networkInterfaces": [ - { - "id": "[resourceId('Microsoft.Network/networkInterfaces', parameters('vmNicName'))]" - } - ] - }, - "diagnosticsProfile": { - "bootDiagnostics": { - "enabled": true, - "storageUri": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('storageAccountResourceGroup')), 'Microsoft.Storage/storageAccounts', parameters('storageAccountName')), '2021-09-01').primaryEndpoints.blob]" - } - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Network/networkInterfaces', parameters('vmNicName'))]" - ] - }, - { - "type": "Microsoft.Compute/virtualMachines/extensions", - "apiVersion": "2023-09-01", - "name": "[format('{0}/{1}', parameters('vmName'), 'DependencyAgentWindows')]", - "location": "[parameters('location')]", - "properties": { - "publisher": "Microsoft.Azure.Monitoring.DependencyAgent", - "type": "DependencyAgentWindows", - "typeHandlerVersion": "9.4", - "autoUpgradeMinorVersion": true, - "enableAutomaticUpgrade": true - }, - "dependsOn": [ - "[resourceId('Microsoft.Compute/virtualMachines', parameters('vmName'))]" - ] - }, - { - "type": "Microsoft.Compute/virtualMachines/extensions", - "apiVersion": "2023-09-01", - "name": "[format('{0}/{1}', parameters('vmName'), 'AzureMonitorWindowsAgent')]", - "location": "[parameters('location')]", - "properties": { - "publisher": "Microsoft.Azure.Monitor", - "type": "AzureMonitorWindowsAgent", - "typeHandlerVersion": "1.0", - "autoUpgradeMinorVersion": true, - "enableAutomaticUpgrade": true - }, - "dependsOn": [ - "[resourceId('Microsoft.Compute/virtualMachines/extensions', parameters('vmName'), 'DependencyAgentWindows')]", - "[resourceId('Microsoft.Compute/virtualMachines', parameters('vmName'))]" - ] - }, - { - "condition": "[parameters('enableMicrosoftEntraIdAuth')]", - "type": "Microsoft.Compute/virtualMachines/extensions", - "apiVersion": "2023-09-01", - "name": "[format('{0}/{1}', parameters('vmName'), 'AADLoginForWindows')]", - "location": "[parameters('location')]", - "properties": { - "publisher": "Microsoft.Azure.ActiveDirectory", - "type": "AADLoginForWindows", - "typeHandlerVersion": "1.0", - "autoUpgradeMinorVersion": false, - "enableAutomaticUpgrade": false - }, - "dependsOn": [ - "[resourceId('Microsoft.Compute/virtualMachines/extensions', parameters('vmName'), 'AzureMonitorWindowsAgent')]", - "[resourceId('Microsoft.Compute/virtualMachines', parameters('vmName'))]" - ] - }, - { - "type": "Microsoft.Insights/dataCollectionRules", - "apiVersion": "2022-06-01", - "name": "DCR-Win-Event-Logs-to-LAW", - "location": "[parameters('location')]", - "kind": "Windows", - "properties": { - "dataFlows": [ - { - "destinations": [ - "logAnalytics" - ], - "streams": [ - "Microsoft-Event" - ] - } - ], - "dataSources": { - "windowsEventLogs": [ - { - "streams": [ - "Microsoft-Event" - ], - "xPathQueries": [ - "Application!*[System[(Level=1 or Level=2 or Level=3 or or Level=0) ]]", - "Security!*[System[(band(Keywords,13510798882111488))]]", - "System!*[System[(Level=1 or Level=2 or Level=3 or or Level=0)]]" - ], - "name": "eventLogsDataSource" - } - ] - }, - "description": "Collect Windows Event Logs and send to Azure Monitor Logs", - "destinations": { - "logAnalytics": [ - { - "name": "logAnalytics", - "workspaceResourceId": "[parameters('workspaceId')]" - } - ] - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Compute/virtualMachines/extensions', parameters('vmName'), 'AADLoginForWindows')]" - ] - }, - { - "type": "Microsoft.Insights/dataCollectionRules", - "apiVersion": "2022-06-01", - "name": "DCR-Win-Perf-to-LAW", - "location": "[parameters('location')]", - "kind": "Windows", - "properties": { - "dataFlows": [ - { - "destinations": [ - "logAnalytics" - ], - "streams": [ - "Microsoft-Perf" - ] - } - ], - "dataSources": { - "performanceCounters": [ - { - "counterSpecifiers": [ - "\\Processor Information(_Total)\\% Processor Time", - "\\Processor Information(_Total)\\% Privileged Time", - "\\Processor Information(_Total)\\% User Time", - "\\Processor Information(_Total)\\Processor Frequency", - "\\System\\Processes", - "\\Process(_Total)\\Thread Count", - "\\Process(_Total)\\Handle Count", - "\\System\\System Up Time", - "\\System\\Context Switches/sec", - "\\System\\Processor Queue Length", - "\\Memory\\% Committed Bytes In Use", - "\\Memory\\Available Bytes", - "\\Memory\\Committed Bytes", - "\\Memory\\Cache Bytes", - "\\Memory\\Pool Paged Bytes", - "\\Memory\\Pool Nonpaged Bytes", - "\\Memory\\Pages/sec", - "\\Memory\\Page Faults/sec", - "\\Process(_Total)\\Working Set", - "\\Process(_Total)\\Working Set - Private", - "\\LogicalDisk(_Total)\\% Disk Time", - "\\LogicalDisk(_Total)\\% Disk Read Time", - "\\LogicalDisk(_Total)\\% Disk Write Time", - "\\LogicalDisk(_Total)\\% Idle Time", - "\\LogicalDisk(_Total)\\Disk Bytes/sec", - "\\LogicalDisk(_Total)\\Disk Read Bytes/sec", - "\\LogicalDisk(_Total)\\Disk Write Bytes/sec", - "\\LogicalDisk(_Total)\\Disk Transfers/sec", - "\\LogicalDisk(_Total)\\Disk Reads/sec", - "\\LogicalDisk(_Total)\\Disk Writes/sec", - "\\LogicalDisk(_Total)\\Avg. Disk sec/Transfer", - "\\LogicalDisk(_Total)\\Avg. Disk sec/Read", - "\\LogicalDisk(_Total)\\Avg. Disk sec/Write", - "\\LogicalDisk(_Total)\\Avg. Disk Queue Length", - "\\LogicalDisk(_Total)\\Avg. Disk Read Queue Length", - "\\LogicalDisk(_Total)\\Avg. Disk Write Queue Length", - "\\LogicalDisk(_Total)\\% Free Space", - "\\LogicalDisk(_Total)\\Free Megabytes", - "\\Network Interface(*)\\Bytes Total/sec", - "\\Network Interface(*)\\Bytes Sent/sec", - "\\Network Interface(*)\\Bytes Received/sec", - "\\Network Interface(*)\\Packets/sec", - "\\Network Interface(*)\\Packets Sent/sec", - "\\Network Interface(*)\\Packets Received/sec", - "\\Network Interface(*)\\Packets Outbound Errors", - "\\Network Interface(*)\\Packets Received Errors" - ], - "name": "perfCounterDataSource60", - "samplingFrequencyInSeconds": 60, - "streams": [ - "Microsoft-Perf" - ] - } - ] - }, - "description": "Collect Performance Counters and send to Azure Monitor Logs.", - "destinations": { - "logAnalytics": [ - { - "name": "logAnalytics", - "workspaceResourceId": "[parameters('workspaceId')]" - } - ] - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Compute/virtualMachines/extensions', parameters('vmName'), 'AADLoginForWindows')]" - ] - }, - { - "type": "Microsoft.Insights/dataCollectionRuleAssociations", - "apiVersion": "2022-06-01", - "scope": "[format('Microsoft.Compute/virtualMachines/{0}', parameters('vmName'))]", - "name": "DCRA-VMSS-WEL-LAW", - "properties": { - "description": "Association of data collection rule. Deleting this association will break the data collection for this virtual machine.", - "dataCollectionRuleId": "[resourceId('Microsoft.Insights/dataCollectionRules', 'DCR-Win-Event-Logs-to-LAW')]" - }, - "dependsOn": [ - "[resourceId('Microsoft.Insights/dataCollectionRules', 'DCR-Win-Event-Logs-to-LAW')]", - "[resourceId('Microsoft.Compute/virtualMachines', parameters('vmName'))]" - ] - }, - { - "type": "Microsoft.Insights/dataCollectionRuleAssociations", - "apiVersion": "2022-06-01", - "scope": "[format('Microsoft.Compute/virtualMachines/{0}', parameters('vmName'))]", - "name": "DCRA-VM-PC-LAW", - "properties": { - "description": "Association of data collection rule. Deleting this association will break the data collection for this virtual machine.", - "dataCollectionRuleId": "[resourceId('Microsoft.Insights/dataCollectionRules', 'DCR-Win-Perf-to-LAW')]" - }, - "dependsOn": [ - "[resourceId('Microsoft.Insights/dataCollectionRules', 'DCR-Win-Perf-to-LAW')]", - "[resourceId('Microsoft.Compute/virtualMachines', parameters('vmName'))]" - ] - }, - { - "condition": "[and(parameters('enableMicrosoftEntraIdAuth'), not(empty(parameters('userObjectId'))))]", - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Compute/virtualMachines/{0}', parameters('vmName'))]", - "name": "[guid(resourceId('Microsoft.Compute/virtualMachines', parameters('vmName')), subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1c0163c0-47e6-4577-8991-ea5c82e286e4'), parameters('userObjectId'))]", - "properties": { - "roleDefinitionId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1c0163c0-47e6-4577-8991-ea5c82e286e4')]", - "principalType": "User", - "principalId": "[parameters('userObjectId')]" - }, - "dependsOn": [ - "[resourceId('Microsoft.Compute/virtualMachines', parameters('vmName'))]" - ] - } - ], - "outputs": { - "name": { - "type": "string", - "value": "[parameters('vmName')]" - }, - "id": { - "type": "string", - "value": "[resourceId('Microsoft.Compute/virtualMachines', parameters('vmName'))]" - } - } - } - }, - "dependsOn": [ - "logAnalyticsWorkspace", - "network", - "storageAccount" - ] - }, - "aiHub": { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[take(format('{0}-ai-hub-deployment', parameters('name')), 64)]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[format('hub-{0}', parameters('name'))]" - }, - "location": { - "value": "[parameters('location')]" - }, - "networkIsolation": { - "value": "[parameters('networkIsolation')]" - }, - "virtualNetworkResourceId": "[if(parameters('networkIsolation'), createObject('value', reference('network').outputs.virtualNetworkId.value), createObject('value', ''))]", - "virtualNetworkSubnetResourceId": "[if(parameters('networkIsolation'), createObject('value', reference('network').outputs.vmSubnetId.value), createObject('value', ''))]", - "logAnalyticsWorkspaceResourceId": { - "value": "[reference('logAnalyticsWorkspace').outputs.resourceId.value]" - }, - "appInsightsResourceId": { - "value": "[reference('applicationInsights').outputs.resourceId.value]" - }, - "containerRegistryResourceId": "[if(parameters('acrEnabled'), createObject('value', reference('containerRegistry').outputs.resourceId.value), createObject('value', null()))]", - "keyVaultResourceId": { - "value": "[reference('keyvault').outputs.resourceId.value]" - }, - "storageAccountResourceId": { - "value": "[reference('storageAccount').outputs.resourceId.value]" - }, - "roleAssignments": "[if(empty(parameters('userObjectId')), createObject('value', createArray()), createObject('value', createArray(createObject('roleDefinitionIdOrName', 'f6c7c914-8db3-469d-8ca1-694a8f32e121', 'principalId', parameters('userObjectId'), 'principalType', 'User'))))]", - "connections": { - "value": "[concat(reference('cognitiveServices').outputs.connections.value, parameters('connections'), if(parameters('searchEnabled'), createArray(createObject('name', reference('aiSearch').outputs.name.value, 'value', null(), 'category', 'CognitiveSearch', 'target', format('https://{0}.search.windows.net/', reference('aiSearch').outputs.name.value), 'connectionProperties', createObject('authType', 'AAD'), 'isSharedToAll', true(), 'metadata', createObject('ApiType', 'Azure', 'ResourceId', reference('aiSearch').outputs.resourceId.value))), createArray()))]" - }, - "tags": { - "value": "[variables('allTags')]" - } - }, - "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.1.42791", - "templateHash": "12408064070292908432" - } - }, - "definitions": { - "_1._10": { - "type": "object", - "properties": { - "authType": { - "type": "string", - "allowedValues": [ - "PAT" - ], - "metadata": { - "description": "Required. The authentication type of the connection target." - } - }, - "credentials": { - "$ref": "#/definitions/_1._18", - "metadata": { - "description": "Required. The credentials for the connection." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/machine-learning-services/workspace:0.10.1", - "originalIdentifier": "#/definitions/_2.patAuthTypeWorkspaceConnectionPropertyType" - } - } - }, - "_1._11": { - "type": "object", - "properties": { - "authType": { - "type": "string", - "allowedValues": [ - "SAS" - ], - "metadata": { - "description": "Required. The authentication type of the connection target." - } - }, - "credentials": { - "$ref": "#/definitions/_1._20", - "metadata": { - "description": "Required. The credentials for the connection." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/machine-learning-services/workspace:0.10.1", - "originalIdentifier": "#/definitions/_2.sasAuthTypeWorkspaceConnectionPropertyType" - } - } - }, - "_1._12": { - "type": "object", - "properties": { - "authType": { - "type": "string", - "allowedValues": [ - "ServicePrincipal" - ], - "metadata": { - "description": "Required. The authentication type of the connection target." - } - }, - "credentials": { - "$ref": "#/definitions/_1._19", - "metadata": { - "description": "Required. The credentials for the connection." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/machine-learning-services/workspace:0.10.1", - "originalIdentifier": "#/definitions/_2.servicePrincipalAuthTypeWorkspaceConnectionPropertyType" - } - } - }, - "_1._13": { - "type": "object", - "properties": { - "authType": { - "type": "string", - "allowedValues": [ - "UsernamePassword" - ], - "metadata": { - "description": "Required. The authentication type of the connection target." - } - }, - "credentials": { - "$ref": "#/definitions/_1._21", - "metadata": { - "description": "Required. The credentials for the connection." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/machine-learning-services/workspace:0.10.1", - "originalIdentifier": "#/definitions/_2.usernamePasswordAuthTypeWorkspaceConnectionPropertyType" - } - } - }, - "_1._14": { - "type": "object", - "properties": { - "accessKeyId": { - "type": "string", - "metadata": { - "description": "Required. The connection access key ID." - } - }, - "secretAccessKey": { - "type": "string", - "metadata": { - "description": "Required. The connection secret access key." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/machine-learning-services/workspace:0.10.1", - "originalIdentifier": "#/definitions/_2.workspaceConnectionAccessKeyType" - } - } - }, - "_1._15": { - "type": "object", - "properties": { - "key": { - "type": "string", - "metadata": { - "description": "Required. The connection API key." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/machine-learning-services/workspace:0.10.1", - "originalIdentifier": "#/definitions/_2.workspaceConnectionApiKeyType" - } - } - }, - "_1._16": { - "type": "object", - "properties": { - "clientId": { - "type": "string", - "metadata": { - "description": "Required. The connection managed identity ID." - } - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "Required. The connection managed identity resource ID." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/machine-learning-services/workspace:0.10.1", - "originalIdentifier": "#/definitions/_2.workspaceConnectionManagedIdentityType" - } - } - }, - "_1._17": { - "type": "object", - "properties": { - "authUrl": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Conditional. The connection auth URL. Required by Concur connection category." - } - }, - "clientId": { - "type": "string", - "minLength": 36, - "maxLength": 36, - "metadata": { - "description": "Required. The connection client ID in the format of UUID." - } - }, - "clientSecret": { - "type": "string", - "metadata": { - "description": "Required. The connection client secret." - } - }, - "developerToken": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Conditional. The connection developer token. Required by GoogleAdWords connection category." - } - }, - "password": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Conditional. The connection password. Required by Concur and ServiceNow connection categories where AccessToken grant type is 'Password'." - } - }, - "refreshToken": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Conditional. The connection refresh token. Required by GoogleBigQuery, GoogleAdWords, Hubspot, QuickBooks, Square, Xero and Zoho connection categories." - } - }, - "tenantId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Required. The connection tenant ID. Required by QuickBooks and Xero connection categories." - } - }, - "username": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Conditional. The connection username. Required by Concur and ServiceNow connection categories where AccessToken grant type is 'Password'." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/machine-learning-services/workspace:0.10.1", - "originalIdentifier": "#/definitions/_2.workspaceConnectionOAuth2Type" - } - } - }, - "_1._18": { - "type": "object", - "properties": { - "pat": { - "type": "string", - "metadata": { - "description": "Required. The connection personal access token." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/machine-learning-services/workspace:0.10.1", - "originalIdentifier": "#/definitions/_2.workspaceConnectionPersonalAccessTokenType" - } - } - }, - "_1._19": { - "type": "object", - "properties": { - "clientId": { - "type": "string", - "metadata": { - "description": "Required. The connection client ID." - } - }, - "clientSecret": { - "type": "string", - "metadata": { - "description": "Required. The connection client secret." - } - }, - "tenantId": { - "type": "string", - "metadata": { - "description": "Required. The connection tenant ID." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/machine-learning-services/workspace:0.10.1", - "originalIdentifier": "#/definitions/_2.workspaceConnectionServicePrincipalType" - } - } - }, - "_1._2": { - "type": "object", - "properties": { - "authType": { - "type": "string", - "allowedValues": [ - "AAD" - ], - "metadata": { - "description": "Required. The authentication type of the connection target." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/machine-learning-services/workspace:0.10.1", - "originalIdentifier": "#/definitions/_2.aadAuthTypeWorkspaceConnectionPropertyType" - } - } - }, - "_1._20": { - "type": "object", - "properties": { - "sas": { - "type": "string", - "metadata": { - "description": "Required. The connection SAS token." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/machine-learning-services/workspace:0.10.1", - "originalIdentifier": "#/definitions/_2.workspaceConnectionSharedAccessSignatureType" - } - } - }, - "_1._21": { - "type": "object", - "properties": { - "password": { - "type": "string", - "metadata": { - "description": "Required. The connection password." - } - }, - "securityToken": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Conditional. The connection security token. Required by connections like SalesForce for extra security in addition to 'UsernamePassword'." - } - }, - "username": { - "type": "string", - "metadata": { - "description": "Required. The connection username." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/machine-learning-services/workspace:0.10.1", - "originalIdentifier": "#/definitions/_2.workspaceConnectionUsernamePasswordType" - } - } - }, - "_1._3": { - "type": "object", - "properties": { - "authType": { - "type": "string", - "allowedValues": [ - "AccessKey" - ], - "metadata": { - "description": "Required. The authentication type of the connection target." - } - }, - "credentials": { - "$ref": "#/definitions/_1._14", - "metadata": { - "description": "Required. The credentials for the connection." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/machine-learning-services/workspace:0.10.1", - "originalIdentifier": "#/definitions/_2.accessKeyAuthTypeWorkspaceConnectionPropertyType" - } - } - }, - "_1._4": { - "type": "object", - "properties": { - "authType": { - "type": "string", - "allowedValues": [ - "ApiKey" - ], - "metadata": { - "description": "Required. The authentication type of the connection target." - } - }, - "credentials": { - "$ref": "#/definitions/_1._15", - "metadata": { - "description": "Required. The credentials for the connection." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/machine-learning-services/workspace:0.10.1", - "originalIdentifier": "#/definitions/_2.apiKeyAuthWorkspaceConnectionPropertyType" - } - } - }, - "_1._5": { - "type": "object", - "properties": { - "keys": { - "type": "object", - "properties": {}, - "additionalProperties": { - "type": "string", - "metadata": { - "description": "Required. Key-value pairs for the custom keys." - } - }, - "metadata": { - "description": "Required. The custom keys for the connection." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/machine-learning-services/workspace:0.10.1", - "originalIdentifier": "#/definitions/_2.customKeysType" - } - } - }, - "_1._6": { - "type": "object", - "properties": { - "authType": { - "type": "string", - "allowedValues": [ - "CustomKeys" - ], - "metadata": { - "description": "Required. The authentication type of the connection target." - } - }, - "credentials": { - "$ref": "#/definitions/_1._5", - "metadata": { - "description": "Required. The credentials for the connection." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/machine-learning-services/workspace:0.10.1", - "originalIdentifier": "#/definitions/_2.customKeysWorkspaceConnectionPropertyType" - } - } - }, - "_1._7": { - "type": "object", - "properties": { - "authType": { - "type": "string", - "allowedValues": [ - "ManagedIdentity" - ], - "metadata": { - "description": "Required. The authentication type of the connection target." - } - }, - "credentials": { - "$ref": "#/definitions/_1._16", - "metadata": { - "description": "Required. The credentials for the connection." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/machine-learning-services/workspace:0.10.1", - "originalIdentifier": "#/definitions/_2.managedIdentityAuthTypeWorkspaceConnectionPropertyType" - } - } - }, - "_1._8": { - "type": "object", - "properties": { - "authType": { - "type": "string", - "allowedValues": [ - "None" - ], - "metadata": { - "description": "Required. The authentication type of the connection target." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/machine-learning-services/workspace:0.10.1", - "originalIdentifier": "#/definitions/_2.noneAuthTypeWorkspaceConnectionPropertyType" - } - } - }, - "_1._9": { - "type": "object", - "properties": { - "authType": { - "type": "string", - "allowedValues": [ - "OAuth2" - ], - "metadata": { - "description": "Required. The authentication type of the connection target." - } - }, - "credentials": { - "$ref": "#/definitions/_1._17", - "metadata": { - "description": "Required. The credentials for the connection." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/machine-learning-services/workspace:0.10.1", - "originalIdentifier": "#/definitions/_2.oauth2AuthTypeWorkspaceConnectionPropertyType" - } - } - }, - "_1.categoryType": { - "type": "string", - "allowedValues": [ - "ADLSGen2", - "AIServices", - "AmazonMws", - "AmazonRdsForOracle", - "AmazonRdsForSqlServer", - "AmazonRedshift", - "AmazonS3Compatible", - "ApiKey", - "AzureBlob", - "AzureDataExplorer", - "AzureDatabricksDeltaLake", - "AzureMariaDb", - "AzureMySqlDb", - "AzureOneLake", - "AzureOpenAI", - "AzurePostgresDb", - "AzureSqlDb", - "AzureSqlMi", - "AzureSynapseAnalytics", - "AzureTableStorage", - "BingLLMSearch", - "Cassandra", - "CognitiveSearch", - "CognitiveService", - "Concur", - "ContainerRegistry", - "CosmosDb", - "CosmosDbMongoDbApi", - "Couchbase", - "CustomKeys", - "Db2", - "Drill", - "Dynamics", - "DynamicsAx", - "DynamicsCrm", - "Eloqua", - "FileServer", - "FtpServer", - "GenericContainerRegistry", - "GenericHttp", - "GenericRest", - "Git", - "GoogleAdWords", - "GoogleBigQuery", - "GoogleCloudStorage", - "Greenplum", - "Hbase", - "Hdfs", - "Hive", - "Hubspot", - "Impala", - "Informix", - "Jira", - "Magento", - "MariaDb", - "Marketo", - "MicrosoftAccess", - "MongoDbAtlas", - "MongoDbV2", - "MySql", - "Netezza", - "ODataRest", - "Odbc", - "Office365", - "OpenAI", - "Oracle", - "OracleCloudStorage", - "OracleServiceCloud", - "PayPal", - "Phoenix", - "PostgreSql", - "Presto", - "PythonFeed", - "QuickBooks", - "Redis", - "Responsys", - "S3", - "Salesforce", - "SalesforceMarketingCloud", - "SalesforceServiceCloud", - "SapBw", - "SapCloudForCustomer", - "SapEcc", - "SapHana", - "SapOpenHub", - "SapTable", - "Serp", - "Serverless", - "ServiceNow", - "Sftp", - "SharePointOnlineList", - "Shopify", - "Snowflake", - "Spark", - "SqlServer", - "Square", - "Sybase", - "Teradata", - "Vertica", - "WebTable", - "Xero", - "Zoho" - ], - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/machine-learning-services/workspace:0.10.1" - } - } - }, - "_1.connectionPropertyType": { - "type": "secureObject", - "discriminator": { - "propertyName": "authType", - "mapping": { - "AAD": { - "$ref": "#/definitions/_1._2" - }, - "AccessKey": { - "$ref": "#/definitions/_1._3" - }, - "ApiKey": { - "$ref": "#/definitions/_1._4" - }, - "CustomKeys": { - "$ref": "#/definitions/_1._6" - }, - "ManagedIdentity": { - "$ref": "#/definitions/_1._7" - }, - "None": { - "$ref": "#/definitions/_1._8" - }, - "OAuth2": { - "$ref": "#/definitions/_1._9" - }, - "PAT": { - "$ref": "#/definitions/_1._10" - }, - "SAS": { - "$ref": "#/definitions/_1._11" - }, - "ServicePrincipal": { - "$ref": "#/definitions/_1._12" - }, - "UsernamePassword": { - "$ref": "#/definitions/_1._13" - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/machine-learning-services/workspace:0.10.1" - } - } - }, - "connectionType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. Name of the connection to create." - } - }, - "category": { - "$ref": "#/definitions/_1.categoryType", - "metadata": { - "description": "Required. Category of the connection." - } - }, - "expiryTime": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The expiry time of the connection." - } - }, - "isSharedToAll": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Indicates whether the connection is shared to all users in the workspace." - } - }, - "metadata": { - "type": "object", - "properties": {}, - "additionalProperties": { - "type": "string", - "metadata": { - "description": "Required. The metadata key-value pairs." - } - }, - "nullable": true, - "metadata": { - "description": "Optional. User metadata for the connection." - } - }, - "sharedUserList": { - "type": "array", - "items": { - "type": "string" - }, - "nullable": true, - "metadata": { - "description": "Optional. The shared user list of the connection." - } - }, - "target": { - "type": "string", - "metadata": { - "description": "Required. The target of the connection." - } - }, - "value": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Value details of the workspace connection." - } - }, - "connectionProperties": { - "$ref": "#/definitions/_1.connectionPropertyType", - "metadata": { - "description": "Required. The properties of the connection, specific to the auth type." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/machine-learning-services/workspace:0.10.1" - } - } - }, - "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.5.1" - } - } - } - }, - "parameters": { - "name": { - "type": "string", - "metadata": { - "description": "Name of the AI Hub." - } - }, - "location": { - "type": "string", - "metadata": { - "description": "Specifies the location for all the Azure resources." - } - }, - "tags": { - "type": "object", - "defaultValue": {}, - "metadata": { - "description": "Optional. Tags to be applied to the resources." - } - }, - "virtualNetworkResourceId": { - "type": "string", - "metadata": { - "description": "Resource ID of the virtual network to link the private DNS zones." - } - }, - "virtualNetworkSubnetResourceId": { - "type": "string", - "metadata": { - "description": "Resource ID of the subnet for the private endpoint." - } - }, - "logAnalyticsWorkspaceResourceId": { - "type": "string", - "metadata": { - "description": "Resource ID of the Log Analytics workspace to use for diagnostic settings." - } - }, - "appInsightsResourceId": { - "type": "string", - "metadata": { - "description": "Resource ID of the Application Insights resource for the Hub." - } - }, - "keyVaultResourceId": { - "type": "string", - "metadata": { - "description": "Resource ID of the Key Vault for the Hub." - } - }, - "storageAccountResourceId": { - "type": "string", - "metadata": { - "description": "Resource ID of the Storage Account for the Hub." - } - }, - "containerRegistryResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Resource ID of the Container Registry for the Hub." - } - }, - "networkIsolation": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Specifies whether network isolation is enabled. This will create a private endpoint for the AI Hub and link the private DNS zone." - } - }, - "roleAssignments": { - "type": "array", - "items": { - "$ref": "#/definitions/roleAssignmentType" - }, - "nullable": true, - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "connections": { - "type": "array", - "items": { - "$ref": "#/definitions/connectionType" - }, - "nullable": true, - "metadata": { - "description": "List of connections to apply to the workspace." - } - } - }, - "variables": { - "nameFormatted": "[toLower(parameters('name'))]" - }, - "resources": { - "mlApiPrivateDnsZone": { - "condition": "[parameters('networkIsolation')]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "private-dns-mlapi-deployment", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[format('privatelink.api.{0}', if(equals(toLower(environment().name), 'azureusgovernment'), 'ml.azure.us', 'azureml.ms'))]" - }, - "virtualNetworkLinks": { - "value": [ - { - "virtualNetworkResourceId": "[parameters('virtualNetworkResourceId')]" - } - ] - }, - "tags": { - "value": "[parameters('tags')]" - } - }, - "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.32.4.45862", - "templateHash": "83178825086050429" - }, - "name": "Private DNS Zones", - "description": "This module deploys a Private DNS zone.", - "owner": "Azure/module-maintainers" - }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } - } - } - }, - "nullable": true - }, - "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." - } - } - }, - "nullable": true - }, - "aType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } - }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "aRecords": { - "type": "array", - "items": { - "type": "object", - "properties": { - "ipv4Address": { - "type": "string", - "metadata": { - "description": "Required. The IPv4 address of this A record." - } - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. The list of A records in the record set." - } - } - } - }, - "nullable": true - }, - "aaaaType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } - }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "aaaaRecords": { - "type": "array", - "items": { - "type": "object", - "properties": { - "ipv6Address": { - "type": "string", - "metadata": { - "description": "Required. The IPv6 address of this AAAA record." - } - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. The list of AAAA records in the record set." - } - } - } - }, - "nullable": true - }, - "cnameType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } - }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "cnameRecord": { - "type": "object", - "properties": { - "cname": { - "type": "string", - "metadata": { - "description": "Required. The canonical name of the CNAME record." - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. The CNAME record in the record set." - } - } - } - }, - "nullable": true - }, - "mxType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } - }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "mxRecords": { - "type": "array", - "items": { - "type": "object", - "properties": { - "exchange": { - "type": "string", - "metadata": { - "description": "Required. The domain name of the mail host for this MX record." - } - }, - "preference": { - "type": "int", - "metadata": { - "description": "Required. The preference value for this MX record." - } - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. The list of MX records in the record set." - } - } - } - }, - "nullable": true - }, - "ptrType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } - }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "ptrRecords": { - "type": "array", - "items": { - "type": "object", - "properties": { - "ptrdname": { - "type": "string", - "metadata": { - "description": "Required. The PTR target domain name for this PTR record." - } - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. The list of PTR records in the record set." - } - } - } - }, - "nullable": true - }, - "soaType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } - }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "soaRecord": { - "type": "object", - "properties": { - "email": { - "type": "string", - "metadata": { - "description": "Required. The email contact for this SOA record." - } - }, - "expireTime": { - "type": "int", - "metadata": { - "description": "Required. The expire time for this SOA record." - } - }, - "host": { - "type": "string", - "metadata": { - "description": "Required. The domain name of the authoritative name server for this SOA record." - } - }, - "minimumTtl": { - "type": "int", - "metadata": { - "description": "Required. The minimum value for this SOA record. By convention this is used to determine the negative caching duration." - } - }, - "refreshTime": { - "type": "int", - "metadata": { - "description": "Required. The refresh value for this SOA record." - } - }, - "retryTime": { - "type": "int", - "metadata": { - "description": "Required. The retry time for this SOA record." - } - }, - "serialNumber": { - "type": "int", - "metadata": { - "description": "Required. The serial number for this SOA record." - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. The SOA record in the record set." - } - } - } - }, - "nullable": true - }, - "srvType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } - }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "srvRecords": { - "type": "array", - "items": { - "type": "object", - "properties": { - "priority": { - "type": "int", - "metadata": { - "description": "Required. The priority value for this SRV record." - } - }, - "weight": { - "type": "int", - "metadata": { - "description": "Required. The weight value for this SRV record." - } - }, - "port": { - "type": "int", - "metadata": { - "description": "Required. The port value for this SRV record." - } - }, - "target": { - "type": "string", - "metadata": { - "description": "Required. The target domain name for this SRV record." - } - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. The list of SRV records in the record set." - } - } - } - }, - "nullable": true - }, - "txtType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } - }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "txtRecords": { - "type": "array", - "items": { - "type": "object", - "properties": { - "value": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. The text value of this TXT record." - } - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. The list of TXT records in the record set." - } - } - } - }, - "nullable": true - }, - "virtualNetworkLinkType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "minLength": 1, - "maxLength": 80, - "metadata": { - "description": "Optional. The resource name." - } - }, - "virtualNetworkResourceId": { - "type": "string", - "metadata": { - "description": "Required. The resource ID of the virtual network to link." - } - }, - "location": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The Azure Region where the resource lives." - } - }, - "registrationEnabled": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Is auto-registration of virtual machine records in the virtual network in the Private DNS zone enabled?." - } - }, - "tags": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Resource tags." - } - }, - "resolutionPolicy": { - "type": "string", - "allowedValues": [ - "Default", - "NxDomainRedirect" - ], - "nullable": true, - "metadata": { - "description": "Optional. The resolution type of the private-dns-zone fallback machanism." - } - } - } - }, - "nullable": true - } - }, - "parameters": { - "name": { - "type": "string", - "metadata": { - "description": "Required. Private DNS zone name." - } - }, - "a": { - "$ref": "#/definitions/aType", - "metadata": { - "description": "Optional. Array of A records." - } - }, - "aaaa": { - "$ref": "#/definitions/aaaaType", - "metadata": { - "description": "Optional. Array of AAAA records." - } - }, - "cname": { - "$ref": "#/definitions/cnameType", - "metadata": { - "description": "Optional. Array of CNAME records." - } - }, - "mx": { - "$ref": "#/definitions/mxType", - "metadata": { - "description": "Optional. Array of MX records." - } - }, - "ptr": { - "$ref": "#/definitions/ptrType", - "metadata": { - "description": "Optional. Array of PTR records." - } - }, - "soa": { - "$ref": "#/definitions/soaType", - "metadata": { - "description": "Optional. Array of SOA records." - } - }, - "srv": { - "$ref": "#/definitions/srvType", - "metadata": { - "description": "Optional. Array of SRV records." - } - }, - "txt": { - "$ref": "#/definitions/txtType", - "metadata": { - "description": "Optional. Array of TXT records." - } - }, - "virtualNetworkLinks": { - "$ref": "#/definitions/virtualNetworkLinkType", - "metadata": { - "description": "Optional. Array of custom objects describing vNet links of the DNS zone. Each object should contain properties 'virtualNetworkResourceId' and 'registrationEnabled'. The 'vnetResourceId' is a resource ID of a vNet to link, 'registrationEnabled' (bool) enables automatic DNS registration in the zone for the linked vNet." - } - }, - "location": { - "type": "string", - "defaultValue": "global", - "metadata": { - "description": "Optional. The location of the PrivateDNSZone. Should be global." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "tags": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Tags of the resource." - } - }, - "lock": { - "$ref": "#/definitions/lockType", - "metadata": { - "description": "Optional. The lock settings of the service." - } - }, - "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')]", - "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": "2024-03-01", - "name": "[format('46d3xbcp.res.network-privatednszone.{0}.{1}', replace('0.7.0', '.', '-'), 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" - } - } - } - } - }, - "privateDnsZone": { - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('name')]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]" - }, - "privateDnsZone_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/privateDnsZones/{0}', parameters('name'))]", - "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", - "properties": { - "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", - "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" - }, - "dependsOn": [ - "privateDnsZone" - ] - }, - "privateDnsZone_roleAssignments": { - "copy": { - "name": "privateDnsZone_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}', parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones', 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": [ - "privateDnsZone" - ] - }, - "privateDnsZone_A": { - "copy": { - "name": "privateDnsZone_A", - "count": "[length(coalesce(parameters('a'), createArray()))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-ARecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "privateDnsZoneName": { - "value": "[parameters('name')]" - }, - "name": { - "value": "[coalesce(parameters('a'), createArray())[copyIndex()].name]" - }, - "aRecords": { - "value": "[tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'aRecords')]" - }, - "metadata": { - "value": "[tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'metadata')]" - }, - "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'ttl'), 3600)]" - }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'roleAssignments')]" - } - }, - "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.32.4.45862", - "templateHash": "2531120132215940282" - }, - "name": "Private DNS Zone A record", - "description": "This module deploys a Private DNS Zone A record.", - "owner": "Azure/module-maintainers" - }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } - } - } - }, - "nullable": true - } - }, - "parameters": { - "privateDnsZoneName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the A record." - } - }, - "aRecords": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. The list of A records in the record set." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata attached to the record set." - } - }, - "ttl": { - "type": "int", - "defaultValue": 3600, - "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - } - }, - "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')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", - "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" - } - }, - "resources": { - "privateDnsZone": { - "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" - }, - "A": { - "type": "Microsoft.Network/privateDnsZones/A", - "apiVersion": "2020-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "properties": { - "aRecords": "[parameters('aRecords')]", - "metadata": "[parameters('metadata')]", - "ttl": "[parameters('ttl')]" - } - }, - "A_roleAssignments": { - "copy": { - "name": "A_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/A/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/A', parameters('privateDnsZoneName'), 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": [ - "A" - ] - } - }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the deployed A record." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the deployed A record." - }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/A', parameters('privateDnsZoneName'), parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group of the deployed A record." - }, - "value": "[resourceGroup().name]" - } - } - } - }, - "dependsOn": [ - "privateDnsZone" - ] - }, - "privateDnsZone_AAAA": { - "copy": { - "name": "privateDnsZone_AAAA", - "count": "[length(coalesce(parameters('aaaa'), createArray()))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-AAAARecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "privateDnsZoneName": { - "value": "[parameters('name')]" - }, - "name": { - "value": "[coalesce(parameters('aaaa'), createArray())[copyIndex()].name]" - }, - "aaaaRecords": { - "value": "[tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'aaaaRecords')]" - }, - "metadata": { - "value": "[tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'metadata')]" - }, - "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'ttl'), 3600)]" - }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'roleAssignments')]" - } - }, - "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.32.4.45862", - "templateHash": "16709340450244912125" - }, - "name": "Private DNS Zone AAAA record", - "description": "This module deploys a Private DNS Zone AAAA record.", - "owner": "Azure/module-maintainers" - }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } - } - } - }, - "nullable": true - } - }, - "parameters": { - "privateDnsZoneName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the AAAA record." - } - }, - "aaaaRecords": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. The list of AAAA records in the record set." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata attached to the record set." - } - }, - "ttl": { - "type": "int", - "defaultValue": 3600, - "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - } - }, - "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')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", - "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" - } - }, - "resources": { - "privateDnsZone": { - "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" - }, - "AAAA": { - "type": "Microsoft.Network/privateDnsZones/AAAA", - "apiVersion": "2020-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "properties": { - "aaaaRecords": "[parameters('aaaaRecords')]", - "metadata": "[parameters('metadata')]", - "ttl": "[parameters('ttl')]" - } - }, - "AAAA_roleAssignments": { - "copy": { - "name": "AAAA_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/AAAA/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/AAAA', parameters('privateDnsZoneName'), 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": [ - "AAAA" - ] - } - }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the deployed AAAA record." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the deployed AAAA record." - }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/AAAA', parameters('privateDnsZoneName'), parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group of the deployed AAAA record." - }, - "value": "[resourceGroup().name]" - } - } - } - }, - "dependsOn": [ - "privateDnsZone" - ] - }, - "privateDnsZone_CNAME": { - "copy": { - "name": "privateDnsZone_CNAME", - "count": "[length(coalesce(parameters('cname'), createArray()))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-CNAMERecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "privateDnsZoneName": { - "value": "[parameters('name')]" - }, - "name": { - "value": "[coalesce(parameters('cname'), createArray())[copyIndex()].name]" - }, - "cnameRecord": { - "value": "[tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'cnameRecord')]" - }, - "metadata": { - "value": "[tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'metadata')]" - }, - "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'ttl'), 3600)]" - }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'roleAssignments')]" - } - }, - "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.32.4.45862", - "templateHash": "9976020649752073181" - }, - "name": "Private DNS Zone CNAME record", - "description": "This module deploys a Private DNS Zone CNAME record.", - "owner": "Azure/module-maintainers" - }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } - } - } - }, - "nullable": true - } - }, - "parameters": { - "privateDnsZoneName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the CNAME record." - } - }, - "cnameRecord": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. A CNAME record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata attached to the record set." - } - }, - "ttl": { - "type": "int", - "defaultValue": 3600, - "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - } - }, - "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')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", - "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" - } - }, - "resources": { - "privateDnsZone": { - "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" - }, - "CNAME": { - "type": "Microsoft.Network/privateDnsZones/CNAME", - "apiVersion": "2020-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "properties": { - "cnameRecord": "[parameters('cnameRecord')]", - "metadata": "[parameters('metadata')]", - "ttl": "[parameters('ttl')]" - } - }, - "CNAME_roleAssignments": { - "copy": { - "name": "CNAME_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/CNAME/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/CNAME', parameters('privateDnsZoneName'), 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": [ - "CNAME" - ] - } - }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the deployed CNAME record." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the deployed CNAME record." - }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/CNAME', parameters('privateDnsZoneName'), parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group of the deployed CNAME record." - }, - "value": "[resourceGroup().name]" - } - } - } - }, - "dependsOn": [ - "privateDnsZone" - ] - }, - "privateDnsZone_MX": { - "copy": { - "name": "privateDnsZone_MX", - "count": "[length(coalesce(parameters('mx'), createArray()))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-MXRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "privateDnsZoneName": { - "value": "[parameters('name')]" - }, - "name": { - "value": "[coalesce(parameters('mx'), createArray())[copyIndex()].name]" - }, - "metadata": { - "value": "[tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'metadata')]" - }, - "mxRecords": { - "value": "[tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'mxRecords')]" - }, - "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'ttl'), 3600)]" - }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'roleAssignments')]" - } - }, - "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.32.4.45862", - "templateHash": "2520323624213076361" - }, - "name": "Private DNS Zone MX record", - "description": "This module deploys a Private DNS Zone MX record.", - "owner": "Azure/module-maintainers" - }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } - } - } - }, - "nullable": true - } - }, - "parameters": { - "privateDnsZoneName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the MX record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata attached to the record set." - } - }, - "mxRecords": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. The list of MX records in the record set." - } - }, - "ttl": { - "type": "int", - "defaultValue": 3600, - "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - } - }, - "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')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", - "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" - } - }, - "resources": { - "privateDnsZone": { - "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" - }, - "MX": { - "type": "Microsoft.Network/privateDnsZones/MX", - "apiVersion": "2020-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "properties": { - "metadata": "[parameters('metadata')]", - "mxRecords": "[parameters('mxRecords')]", - "ttl": "[parameters('ttl')]" - } - }, - "MX_roleAssignments": { - "copy": { - "name": "MX_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/MX/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/MX', parameters('privateDnsZoneName'), 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": [ - "MX" - ] - } - }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the deployed MX record." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the deployed MX record." - }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/MX', parameters('privateDnsZoneName'), parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group of the deployed MX record." - }, - "value": "[resourceGroup().name]" - } - } - } - }, - "dependsOn": [ - "privateDnsZone" - ] - }, - "privateDnsZone_PTR": { - "copy": { - "name": "privateDnsZone_PTR", - "count": "[length(coalesce(parameters('ptr'), createArray()))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-PTRRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "privateDnsZoneName": { - "value": "[parameters('name')]" - }, - "name": { - "value": "[coalesce(parameters('ptr'), createArray())[copyIndex()].name]" - }, - "metadata": { - "value": "[tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'metadata')]" - }, - "ptrRecords": { - "value": "[tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'ptrRecords')]" - }, - "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'ttl'), 3600)]" - }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'roleAssignments')]" - } - }, - "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.32.4.45862", - "templateHash": "3080404733048745471" - }, - "name": "Private DNS Zone PTR record", - "description": "This module deploys a Private DNS Zone PTR record.", - "owner": "Azure/module-maintainers" - }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } - } - } - }, - "nullable": true - } - }, - "parameters": { - "privateDnsZoneName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the PTR record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata attached to the record set." - } - }, - "ptrRecords": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. The list of PTR records in the record set." - } - }, - "ttl": { - "type": "int", - "defaultValue": 3600, - "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - } - }, - "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')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", - "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" - } - }, - "resources": { - "privateDnsZone": { - "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" - }, - "PTR": { - "type": "Microsoft.Network/privateDnsZones/PTR", - "apiVersion": "2020-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "properties": { - "metadata": "[parameters('metadata')]", - "ptrRecords": "[parameters('ptrRecords')]", - "ttl": "[parameters('ttl')]" - } - }, - "PTR_roleAssignments": { - "copy": { - "name": "PTR_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/PTR/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/PTR', parameters('privateDnsZoneName'), 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": [ - "PTR" - ] - } - }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the deployed PTR record." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the deployed PTR record." - }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/PTR', parameters('privateDnsZoneName'), parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group of the deployed PTR record." - }, - "value": "[resourceGroup().name]" - } - } - } - }, - "dependsOn": [ - "privateDnsZone" - ] - }, - "privateDnsZone_SOA": { - "copy": { - "name": "privateDnsZone_SOA", - "count": "[length(coalesce(parameters('soa'), createArray()))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-SOARecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "privateDnsZoneName": { - "value": "[parameters('name')]" - }, - "name": { - "value": "[coalesce(parameters('soa'), createArray())[copyIndex()].name]" - }, - "metadata": { - "value": "[tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'metadata')]" - }, - "soaRecord": { - "value": "[tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'soaRecord')]" - }, - "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'ttl'), 3600)]" - }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'roleAssignments')]" - } - }, - "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.32.4.45862", - "templateHash": "6653951445614700931" - }, - "name": "Private DNS Zone SOA record", - "description": "This module deploys a Private DNS Zone SOA record.", - "owner": "Azure/module-maintainers" - }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } - } - } - }, - "nullable": true - } - }, - "parameters": { - "privateDnsZoneName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the SOA record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata attached to the record set." - } - }, - "soaRecord": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. A SOA record." - } - }, - "ttl": { - "type": "int", - "defaultValue": 3600, - "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - } - }, - "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')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", - "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" - } - }, - "resources": { - "privateDnsZone": { - "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" - }, - "SOA": { - "type": "Microsoft.Network/privateDnsZones/SOA", - "apiVersion": "2020-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "properties": { - "metadata": "[parameters('metadata')]", - "soaRecord": "[parameters('soaRecord')]", - "ttl": "[parameters('ttl')]" - } - }, - "SOA_roleAssignments": { - "copy": { - "name": "SOA_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/SOA/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/SOA', parameters('privateDnsZoneName'), 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": [ - "SOA" - ] - } - }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the deployed SOA record." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the deployed SOA record." - }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/SOA', parameters('privateDnsZoneName'), parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group of the deployed SOA record." - }, - "value": "[resourceGroup().name]" - } - } - } - }, - "dependsOn": [ - "privateDnsZone" - ] - }, - "privateDnsZone_SRV": { - "copy": { - "name": "privateDnsZone_SRV", - "count": "[length(coalesce(parameters('srv'), createArray()))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-SRVRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "privateDnsZoneName": { - "value": "[parameters('name')]" - }, - "name": { - "value": "[coalesce(parameters('srv'), createArray())[copyIndex()].name]" - }, - "metadata": { - "value": "[tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'metadata')]" - }, - "srvRecords": { - "value": "[tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'srvRecords')]" - }, - "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'ttl'), 3600)]" - }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'roleAssignments')]" - } - }, - "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.32.4.45862", - "templateHash": "5790774778713328446" - }, - "name": "Private DNS Zone SRV record", - "description": "This module deploys a Private DNS Zone SRV record.", - "owner": "Azure/module-maintainers" - }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } - } - } - }, - "nullable": true - } - }, - "parameters": { - "privateDnsZoneName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the SRV record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata attached to the record set." - } - }, - "srvRecords": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. The list of SRV records in the record set." - } - }, - "ttl": { - "type": "int", - "defaultValue": 3600, - "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - } - }, - "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')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", - "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" - } - }, - "resources": { - "privateDnsZone": { - "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" - }, - "SRV": { - "type": "Microsoft.Network/privateDnsZones/SRV", - "apiVersion": "2020-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "properties": { - "metadata": "[parameters('metadata')]", - "srvRecords": "[parameters('srvRecords')]", - "ttl": "[parameters('ttl')]" - } - }, - "SRV_roleAssignments": { - "copy": { - "name": "SRV_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/SRV/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/SRV', parameters('privateDnsZoneName'), 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": [ - "SRV" - ] - } - }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the deployed SRV record." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the deployed SRV record." - }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/SRV', parameters('privateDnsZoneName'), parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group of the deployed SRV record." - }, - "value": "[resourceGroup().name]" - } - } - } - }, - "dependsOn": [ - "privateDnsZone" - ] - }, - "privateDnsZone_TXT": { - "copy": { - "name": "privateDnsZone_TXT", - "count": "[length(coalesce(parameters('txt'), createArray()))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-TXTRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "privateDnsZoneName": { - "value": "[parameters('name')]" - }, - "name": { - "value": "[coalesce(parameters('txt'), createArray())[copyIndex()].name]" - }, - "metadata": { - "value": "[tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'metadata')]" - }, - "txtRecords": { - "value": "[tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'txtRecords')]" - }, - "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'ttl'), 3600)]" - }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'roleAssignments')]" - } - }, - "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.32.4.45862", - "templateHash": "1855369119498044639" - }, - "name": "Private DNS Zone TXT record", - "description": "This module deploys a Private DNS Zone TXT record.", - "owner": "Azure/module-maintainers" - }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } - } - } - }, - "nullable": true - } - }, - "parameters": { - "privateDnsZoneName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the TXT record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata attached to the record set." - } - }, - "ttl": { - "type": "int", - "defaultValue": 3600, - "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." - } - }, - "txtRecords": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. The list of TXT records in the record set." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - } - }, - "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')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", - "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" - } - }, - "resources": { - "privateDnsZone": { - "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" - }, - "TXT": { - "type": "Microsoft.Network/privateDnsZones/TXT", - "apiVersion": "2020-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "properties": { - "metadata": "[parameters('metadata')]", - "ttl": "[parameters('ttl')]", - "txtRecords": "[parameters('txtRecords')]" - } - }, - "TXT_roleAssignments": { - "copy": { - "name": "TXT_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/TXT/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/TXT', parameters('privateDnsZoneName'), 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": [ - "TXT" - ] - } - }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the deployed TXT record." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the deployed TXT record." - }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/TXT', parameters('privateDnsZoneName'), parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group of the deployed TXT record." - }, - "value": "[resourceGroup().name]" - } - } - } - }, - "dependsOn": [ - "privateDnsZone" - ] - }, - "privateDnsZone_virtualNetworkLinks": { - "copy": { - "name": "privateDnsZone_virtualNetworkLinks", - "count": "[length(coalesce(parameters('virtualNetworkLinks'), createArray()))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-VirtualNetworkLink-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "privateDnsZoneName": { - "value": "[parameters('name')]" - }, - "name": { - "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'name'), format('{0}-vnetlink', last(split(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()].virtualNetworkResourceId, '/'))))]" - }, - "virtualNetworkResourceId": { - "value": "[coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()].virtualNetworkResourceId]" - }, - "location": { - "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'location'), 'global')]" - }, - "registrationEnabled": { - "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'registrationEnabled'), false())]" - }, - "tags": { - "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" - }, - "resolutionPolicy": { - "value": "[tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'resolutionPolicy')]" - } - }, - "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.32.4.45862", - "templateHash": "15326596012552051215" - }, - "name": "Private DNS Zone Virtual Network Link", - "description": "This module deploys a Private DNS Zone Virtual Network Link.", - "owner": "Azure/module-maintainers" - }, - "parameters": { - "privateDnsZoneName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", - "defaultValue": "[format('{0}-vnetlink', last(split(parameters('virtualNetworkResourceId'), '/')))]", - "metadata": { - "description": "Optional. The name of the virtual network link." - } - }, - "location": { - "type": "string", - "defaultValue": "global", - "metadata": { - "description": "Optional. The location of the PrivateDNSZone. Should be global." - } - }, - "tags": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Tags of the resource." - } - }, - "registrationEnabled": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "Optional. Is auto-registration of virtual machine records in the virtual network in the Private DNS zone enabled?." - } - }, - "virtualNetworkResourceId": { - "type": "string", - "metadata": { - "description": "Required. Link to another virtual network resource ID." - } - }, - "resolutionPolicy": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The resolution policy on the virtual network link. Only applicable for virtual network links to privatelink zones, and for A,AAAA,CNAME queries. When set to `NxDomainRedirect`, Azure DNS resolver falls back to public resolution if private dns query resolution results in non-existent domain response. `Default` is configured as the default option." - } - } - }, - "resources": { - "privateDnsZone": { - "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" - }, - "virtualNetworkLink": { - "type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks", - "apiVersion": "2024-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", - "properties": { - "registrationEnabled": "[parameters('registrationEnabled')]", - "virtualNetwork": { - "id": "[parameters('virtualNetworkResourceId')]" - }, - "resolutionPolicy": "[parameters('resolutionPolicy')]" - } - } - }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the deployed virtual network link." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the deployed virtual network link." - }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/virtualNetworkLinks', parameters('privateDnsZoneName'), parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group of the deployed virtual network link." - }, - "value": "[resourceGroup().name]" - }, - "location": { - "type": "string", - "metadata": { - "description": "The location the resource was deployed into." - }, - "value": "[reference('virtualNetworkLink', '2024-06-01', 'full').location]" - } - } - } - }, - "dependsOn": [ - "privateDnsZone" - ] - } - }, - "outputs": { - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group the private DNS zone was deployed into." - }, - "value": "[resourceGroup().name]" - }, - "name": { - "type": "string", - "metadata": { - "description": "The name of the private DNS zone." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the private DNS zone." - }, - "value": "[resourceId('Microsoft.Network/privateDnsZones', parameters('name'))]" - }, - "location": { - "type": "string", - "metadata": { - "description": "The location the resource was deployed into." - }, - "value": "[reference('privateDnsZone', '2020-06-01', 'full').location]" - } - } - } - } - }, - "mlNotebooksPrivateDnsZone": { - "condition": "[parameters('networkIsolation')]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "private-dns-mlnotebook-deployment", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[format('privatelink.notebooks.{0}', if(equals(toLower(environment().name), 'azureusgovernment'), 'azureml.us', 'azureml.net'))]" - }, - "virtualNetworkLinks": { - "value": [ - { - "virtualNetworkResourceId": "[parameters('virtualNetworkResourceId')]" - } - ] - }, - "tags": { - "value": "[parameters('tags')]" - } - }, - "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.32.4.45862", - "templateHash": "83178825086050429" - }, - "name": "Private DNS Zones", - "description": "This module deploys a Private DNS zone.", - "owner": "Azure/module-maintainers" - }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } - } - } - }, - "nullable": true - }, - "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." - } - } - }, - "nullable": true - }, - "aType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } - }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "aRecords": { - "type": "array", - "items": { - "type": "object", - "properties": { - "ipv4Address": { - "type": "string", - "metadata": { - "description": "Required. The IPv4 address of this A record." - } - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. The list of A records in the record set." - } - } - } - }, - "nullable": true - }, - "aaaaType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } - }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "aaaaRecords": { - "type": "array", - "items": { - "type": "object", - "properties": { - "ipv6Address": { - "type": "string", - "metadata": { - "description": "Required. The IPv6 address of this AAAA record." - } - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. The list of AAAA records in the record set." - } - } - } - }, - "nullable": true - }, - "cnameType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } - }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "cnameRecord": { - "type": "object", - "properties": { - "cname": { - "type": "string", - "metadata": { - "description": "Required. The canonical name of the CNAME record." - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. The CNAME record in the record set." - } - } - } - }, - "nullable": true - }, - "mxType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } - }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "mxRecords": { - "type": "array", - "items": { - "type": "object", - "properties": { - "exchange": { - "type": "string", - "metadata": { - "description": "Required. The domain name of the mail host for this MX record." - } - }, - "preference": { - "type": "int", - "metadata": { - "description": "Required. The preference value for this MX record." - } - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. The list of MX records in the record set." - } - } - } - }, - "nullable": true - }, - "ptrType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } - }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "ptrRecords": { - "type": "array", - "items": { - "type": "object", - "properties": { - "ptrdname": { - "type": "string", - "metadata": { - "description": "Required. The PTR target domain name for this PTR record." - } - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. The list of PTR records in the record set." - } - } - } - }, - "nullable": true - }, - "soaType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } - }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "soaRecord": { - "type": "object", - "properties": { - "email": { - "type": "string", - "metadata": { - "description": "Required. The email contact for this SOA record." - } - }, - "expireTime": { - "type": "int", - "metadata": { - "description": "Required. The expire time for this SOA record." - } - }, - "host": { - "type": "string", - "metadata": { - "description": "Required. The domain name of the authoritative name server for this SOA record." - } - }, - "minimumTtl": { - "type": "int", - "metadata": { - "description": "Required. The minimum value for this SOA record. By convention this is used to determine the negative caching duration." - } - }, - "refreshTime": { - "type": "int", - "metadata": { - "description": "Required. The refresh value for this SOA record." - } - }, - "retryTime": { - "type": "int", - "metadata": { - "description": "Required. The retry time for this SOA record." - } - }, - "serialNumber": { - "type": "int", - "metadata": { - "description": "Required. The serial number for this SOA record." - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. The SOA record in the record set." - } - } - } - }, - "nullable": true - }, - "srvType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } - }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "srvRecords": { - "type": "array", - "items": { - "type": "object", - "properties": { - "priority": { - "type": "int", - "metadata": { - "description": "Required. The priority value for this SRV record." - } - }, - "weight": { - "type": "int", - "metadata": { - "description": "Required. The weight value for this SRV record." - } - }, - "port": { - "type": "int", - "metadata": { - "description": "Required. The port value for this SRV record." - } - }, - "target": { - "type": "string", - "metadata": { - "description": "Required. The target domain name for this SRV record." - } - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. The list of SRV records in the record set." - } - } - } - }, - "nullable": true - }, - "txtType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } - }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "txtRecords": { - "type": "array", - "items": { - "type": "object", - "properties": { - "value": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. The text value of this TXT record." - } - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. The list of TXT records in the record set." - } - } - } - }, - "nullable": true - }, - "virtualNetworkLinkType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "minLength": 1, - "maxLength": 80, - "metadata": { - "description": "Optional. The resource name." - } - }, - "virtualNetworkResourceId": { - "type": "string", - "metadata": { - "description": "Required. The resource ID of the virtual network to link." - } - }, - "location": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The Azure Region where the resource lives." - } - }, - "registrationEnabled": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Is auto-registration of virtual machine records in the virtual network in the Private DNS zone enabled?." - } - }, - "tags": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Resource tags." - } - }, - "resolutionPolicy": { - "type": "string", - "allowedValues": [ - "Default", - "NxDomainRedirect" - ], - "nullable": true, - "metadata": { - "description": "Optional. The resolution type of the private-dns-zone fallback machanism." - } - } - } - }, - "nullable": true - } - }, - "parameters": { - "name": { - "type": "string", - "metadata": { - "description": "Required. Private DNS zone name." - } - }, - "a": { - "$ref": "#/definitions/aType", - "metadata": { - "description": "Optional. Array of A records." - } - }, - "aaaa": { - "$ref": "#/definitions/aaaaType", - "metadata": { - "description": "Optional. Array of AAAA records." - } - }, - "cname": { - "$ref": "#/definitions/cnameType", - "metadata": { - "description": "Optional. Array of CNAME records." - } - }, - "mx": { - "$ref": "#/definitions/mxType", - "metadata": { - "description": "Optional. Array of MX records." - } - }, - "ptr": { - "$ref": "#/definitions/ptrType", - "metadata": { - "description": "Optional. Array of PTR records." - } - }, - "soa": { - "$ref": "#/definitions/soaType", - "metadata": { - "description": "Optional. Array of SOA records." - } - }, - "srv": { - "$ref": "#/definitions/srvType", - "metadata": { - "description": "Optional. Array of SRV records." - } - }, - "txt": { - "$ref": "#/definitions/txtType", - "metadata": { - "description": "Optional. Array of TXT records." - } - }, - "virtualNetworkLinks": { - "$ref": "#/definitions/virtualNetworkLinkType", - "metadata": { - "description": "Optional. Array of custom objects describing vNet links of the DNS zone. Each object should contain properties 'virtualNetworkResourceId' and 'registrationEnabled'. The 'vnetResourceId' is a resource ID of a vNet to link, 'registrationEnabled' (bool) enables automatic DNS registration in the zone for the linked vNet." - } - }, - "location": { - "type": "string", - "defaultValue": "global", - "metadata": { - "description": "Optional. The location of the PrivateDNSZone. Should be global." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "tags": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Tags of the resource." - } - }, - "lock": { - "$ref": "#/definitions/lockType", - "metadata": { - "description": "Optional. The lock settings of the service." - } - }, - "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')]", - "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": "2024-03-01", - "name": "[format('46d3xbcp.res.network-privatednszone.{0}.{1}', replace('0.7.0', '.', '-'), 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" - } - } - } - } - }, - "privateDnsZone": { - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('name')]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]" - }, - "privateDnsZone_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/privateDnsZones/{0}', parameters('name'))]", - "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", - "properties": { - "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", - "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" - }, - "dependsOn": [ - "privateDnsZone" - ] - }, - "privateDnsZone_roleAssignments": { - "copy": { - "name": "privateDnsZone_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}', parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones', 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": [ - "privateDnsZone" - ] - }, - "privateDnsZone_A": { - "copy": { - "name": "privateDnsZone_A", - "count": "[length(coalesce(parameters('a'), createArray()))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-ARecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "privateDnsZoneName": { - "value": "[parameters('name')]" - }, - "name": { - "value": "[coalesce(parameters('a'), createArray())[copyIndex()].name]" - }, - "aRecords": { - "value": "[tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'aRecords')]" - }, - "metadata": { - "value": "[tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'metadata')]" - }, - "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'ttl'), 3600)]" - }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'roleAssignments')]" - } - }, - "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.32.4.45862", - "templateHash": "2531120132215940282" - }, - "name": "Private DNS Zone A record", - "description": "This module deploys a Private DNS Zone A record.", - "owner": "Azure/module-maintainers" - }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } - } - } - }, - "nullable": true - } - }, - "parameters": { - "privateDnsZoneName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the A record." - } - }, - "aRecords": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. The list of A records in the record set." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata attached to the record set." - } - }, - "ttl": { - "type": "int", - "defaultValue": 3600, - "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - } - }, - "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')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", - "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" - } - }, - "resources": { - "privateDnsZone": { - "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" - }, - "A": { - "type": "Microsoft.Network/privateDnsZones/A", - "apiVersion": "2020-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "properties": { - "aRecords": "[parameters('aRecords')]", - "metadata": "[parameters('metadata')]", - "ttl": "[parameters('ttl')]" - } - }, - "A_roleAssignments": { - "copy": { - "name": "A_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/A/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/A', parameters('privateDnsZoneName'), 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": [ - "A" - ] - } - }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the deployed A record." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the deployed A record." - }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/A', parameters('privateDnsZoneName'), parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group of the deployed A record." - }, - "value": "[resourceGroup().name]" - } - } - } - }, - "dependsOn": [ - "privateDnsZone" - ] - }, - "privateDnsZone_AAAA": { - "copy": { - "name": "privateDnsZone_AAAA", - "count": "[length(coalesce(parameters('aaaa'), createArray()))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-AAAARecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "privateDnsZoneName": { - "value": "[parameters('name')]" - }, - "name": { - "value": "[coalesce(parameters('aaaa'), createArray())[copyIndex()].name]" - }, - "aaaaRecords": { - "value": "[tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'aaaaRecords')]" - }, - "metadata": { - "value": "[tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'metadata')]" - }, - "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'ttl'), 3600)]" - }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'roleAssignments')]" - } - }, - "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.32.4.45862", - "templateHash": "16709340450244912125" - }, - "name": "Private DNS Zone AAAA record", - "description": "This module deploys a Private DNS Zone AAAA record.", - "owner": "Azure/module-maintainers" - }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } - } - } - }, - "nullable": true - } - }, - "parameters": { - "privateDnsZoneName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the AAAA record." - } - }, - "aaaaRecords": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. The list of AAAA records in the record set." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata attached to the record set." - } - }, - "ttl": { - "type": "int", - "defaultValue": 3600, - "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - } - }, - "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')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", - "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" - } - }, - "resources": { - "privateDnsZone": { - "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" - }, - "AAAA": { - "type": "Microsoft.Network/privateDnsZones/AAAA", - "apiVersion": "2020-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "properties": { - "aaaaRecords": "[parameters('aaaaRecords')]", - "metadata": "[parameters('metadata')]", - "ttl": "[parameters('ttl')]" - } - }, - "AAAA_roleAssignments": { - "copy": { - "name": "AAAA_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/AAAA/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/AAAA', parameters('privateDnsZoneName'), 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": [ - "AAAA" - ] - } - }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the deployed AAAA record." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the deployed AAAA record." - }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/AAAA', parameters('privateDnsZoneName'), parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group of the deployed AAAA record." - }, - "value": "[resourceGroup().name]" - } - } - } - }, - "dependsOn": [ - "privateDnsZone" - ] - }, - "privateDnsZone_CNAME": { - "copy": { - "name": "privateDnsZone_CNAME", - "count": "[length(coalesce(parameters('cname'), createArray()))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-CNAMERecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "privateDnsZoneName": { - "value": "[parameters('name')]" - }, - "name": { - "value": "[coalesce(parameters('cname'), createArray())[copyIndex()].name]" - }, - "cnameRecord": { - "value": "[tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'cnameRecord')]" - }, - "metadata": { - "value": "[tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'metadata')]" - }, - "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'ttl'), 3600)]" - }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'roleAssignments')]" - } - }, - "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.32.4.45862", - "templateHash": "9976020649752073181" - }, - "name": "Private DNS Zone CNAME record", - "description": "This module deploys a Private DNS Zone CNAME record.", - "owner": "Azure/module-maintainers" - }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } - } - } - }, - "nullable": true - } - }, - "parameters": { - "privateDnsZoneName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the CNAME record." - } - }, - "cnameRecord": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. A CNAME record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata attached to the record set." - } - }, - "ttl": { - "type": "int", - "defaultValue": 3600, - "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - } - }, - "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')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", - "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" - } - }, - "resources": { - "privateDnsZone": { - "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" - }, - "CNAME": { - "type": "Microsoft.Network/privateDnsZones/CNAME", - "apiVersion": "2020-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "properties": { - "cnameRecord": "[parameters('cnameRecord')]", - "metadata": "[parameters('metadata')]", - "ttl": "[parameters('ttl')]" - } - }, - "CNAME_roleAssignments": { - "copy": { - "name": "CNAME_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/CNAME/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/CNAME', parameters('privateDnsZoneName'), 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": [ - "CNAME" - ] - } - }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the deployed CNAME record." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the deployed CNAME record." - }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/CNAME', parameters('privateDnsZoneName'), parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group of the deployed CNAME record." - }, - "value": "[resourceGroup().name]" - } - } - } - }, - "dependsOn": [ - "privateDnsZone" - ] - }, - "privateDnsZone_MX": { - "copy": { - "name": "privateDnsZone_MX", - "count": "[length(coalesce(parameters('mx'), createArray()))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-MXRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "privateDnsZoneName": { - "value": "[parameters('name')]" - }, - "name": { - "value": "[coalesce(parameters('mx'), createArray())[copyIndex()].name]" - }, - "metadata": { - "value": "[tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'metadata')]" - }, - "mxRecords": { - "value": "[tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'mxRecords')]" - }, - "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'ttl'), 3600)]" - }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'roleAssignments')]" - } - }, - "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.32.4.45862", - "templateHash": "2520323624213076361" - }, - "name": "Private DNS Zone MX record", - "description": "This module deploys a Private DNS Zone MX record.", - "owner": "Azure/module-maintainers" - }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } - } - } - }, - "nullable": true - } - }, - "parameters": { - "privateDnsZoneName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the MX record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata attached to the record set." - } - }, - "mxRecords": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. The list of MX records in the record set." - } - }, - "ttl": { - "type": "int", - "defaultValue": 3600, - "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - } - }, - "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')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", - "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" - } - }, - "resources": { - "privateDnsZone": { - "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" - }, - "MX": { - "type": "Microsoft.Network/privateDnsZones/MX", - "apiVersion": "2020-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "properties": { - "metadata": "[parameters('metadata')]", - "mxRecords": "[parameters('mxRecords')]", - "ttl": "[parameters('ttl')]" - } - }, - "MX_roleAssignments": { - "copy": { - "name": "MX_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/MX/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/MX', parameters('privateDnsZoneName'), 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": [ - "MX" - ] - } - }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the deployed MX record." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the deployed MX record." - }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/MX', parameters('privateDnsZoneName'), parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group of the deployed MX record." - }, - "value": "[resourceGroup().name]" - } - } - } - }, - "dependsOn": [ - "privateDnsZone" - ] - }, - "privateDnsZone_PTR": { - "copy": { - "name": "privateDnsZone_PTR", - "count": "[length(coalesce(parameters('ptr'), createArray()))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-PTRRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "privateDnsZoneName": { - "value": "[parameters('name')]" - }, - "name": { - "value": "[coalesce(parameters('ptr'), createArray())[copyIndex()].name]" - }, - "metadata": { - "value": "[tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'metadata')]" - }, - "ptrRecords": { - "value": "[tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'ptrRecords')]" - }, - "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'ttl'), 3600)]" - }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'roleAssignments')]" - } - }, - "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.32.4.45862", - "templateHash": "3080404733048745471" - }, - "name": "Private DNS Zone PTR record", - "description": "This module deploys a Private DNS Zone PTR record.", - "owner": "Azure/module-maintainers" - }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } - } - } - }, - "nullable": true - } - }, - "parameters": { - "privateDnsZoneName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the PTR record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata attached to the record set." - } - }, - "ptrRecords": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. The list of PTR records in the record set." - } - }, - "ttl": { - "type": "int", - "defaultValue": 3600, - "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - } - }, - "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')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", - "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" - } - }, - "resources": { - "privateDnsZone": { - "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" - }, - "PTR": { - "type": "Microsoft.Network/privateDnsZones/PTR", - "apiVersion": "2020-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "properties": { - "metadata": "[parameters('metadata')]", - "ptrRecords": "[parameters('ptrRecords')]", - "ttl": "[parameters('ttl')]" - } - }, - "PTR_roleAssignments": { - "copy": { - "name": "PTR_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/PTR/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/PTR', parameters('privateDnsZoneName'), 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": [ - "PTR" - ] - } - }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the deployed PTR record." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the deployed PTR record." - }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/PTR', parameters('privateDnsZoneName'), parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group of the deployed PTR record." - }, - "value": "[resourceGroup().name]" - } - } - } - }, - "dependsOn": [ - "privateDnsZone" - ] - }, - "privateDnsZone_SOA": { - "copy": { - "name": "privateDnsZone_SOA", - "count": "[length(coalesce(parameters('soa'), createArray()))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-SOARecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "privateDnsZoneName": { - "value": "[parameters('name')]" - }, - "name": { - "value": "[coalesce(parameters('soa'), createArray())[copyIndex()].name]" - }, - "metadata": { - "value": "[tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'metadata')]" - }, - "soaRecord": { - "value": "[tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'soaRecord')]" - }, - "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'ttl'), 3600)]" - }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'roleAssignments')]" - } - }, - "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.32.4.45862", - "templateHash": "6653951445614700931" - }, - "name": "Private DNS Zone SOA record", - "description": "This module deploys a Private DNS Zone SOA record.", - "owner": "Azure/module-maintainers" - }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } - } - } - }, - "nullable": true - } - }, - "parameters": { - "privateDnsZoneName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the SOA record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata attached to the record set." - } - }, - "soaRecord": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. A SOA record." - } - }, - "ttl": { - "type": "int", - "defaultValue": 3600, - "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - } - }, - "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')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", - "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" - } - }, - "resources": { - "privateDnsZone": { - "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" - }, - "SOA": { - "type": "Microsoft.Network/privateDnsZones/SOA", - "apiVersion": "2020-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "properties": { - "metadata": "[parameters('metadata')]", - "soaRecord": "[parameters('soaRecord')]", - "ttl": "[parameters('ttl')]" - } - }, - "SOA_roleAssignments": { - "copy": { - "name": "SOA_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/SOA/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/SOA', parameters('privateDnsZoneName'), 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": [ - "SOA" - ] - } - }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the deployed SOA record." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the deployed SOA record." - }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/SOA', parameters('privateDnsZoneName'), parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group of the deployed SOA record." - }, - "value": "[resourceGroup().name]" - } - } - } - }, - "dependsOn": [ - "privateDnsZone" - ] - }, - "privateDnsZone_SRV": { - "copy": { - "name": "privateDnsZone_SRV", - "count": "[length(coalesce(parameters('srv'), createArray()))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-SRVRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "privateDnsZoneName": { - "value": "[parameters('name')]" - }, - "name": { - "value": "[coalesce(parameters('srv'), createArray())[copyIndex()].name]" - }, - "metadata": { - "value": "[tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'metadata')]" - }, - "srvRecords": { - "value": "[tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'srvRecords')]" - }, - "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'ttl'), 3600)]" - }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'roleAssignments')]" - } - }, - "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.32.4.45862", - "templateHash": "5790774778713328446" - }, - "name": "Private DNS Zone SRV record", - "description": "This module deploys a Private DNS Zone SRV record.", - "owner": "Azure/module-maintainers" - }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } - } - } - }, - "nullable": true - } - }, - "parameters": { - "privateDnsZoneName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the SRV record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata attached to the record set." - } - }, - "srvRecords": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. The list of SRV records in the record set." - } - }, - "ttl": { - "type": "int", - "defaultValue": 3600, - "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - } - }, - "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')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", - "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" - } - }, - "resources": { - "privateDnsZone": { - "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" - }, - "SRV": { - "type": "Microsoft.Network/privateDnsZones/SRV", - "apiVersion": "2020-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "properties": { - "metadata": "[parameters('metadata')]", - "srvRecords": "[parameters('srvRecords')]", - "ttl": "[parameters('ttl')]" - } - }, - "SRV_roleAssignments": { - "copy": { - "name": "SRV_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/SRV/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/SRV', parameters('privateDnsZoneName'), 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": [ - "SRV" - ] - } - }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the deployed SRV record." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the deployed SRV record." - }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/SRV', parameters('privateDnsZoneName'), parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group of the deployed SRV record." - }, - "value": "[resourceGroup().name]" - } - } - } - }, - "dependsOn": [ - "privateDnsZone" - ] - }, - "privateDnsZone_TXT": { - "copy": { - "name": "privateDnsZone_TXT", - "count": "[length(coalesce(parameters('txt'), createArray()))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-TXTRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "privateDnsZoneName": { - "value": "[parameters('name')]" - }, - "name": { - "value": "[coalesce(parameters('txt'), createArray())[copyIndex()].name]" - }, - "metadata": { - "value": "[tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'metadata')]" - }, - "txtRecords": { - "value": "[tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'txtRecords')]" - }, - "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'ttl'), 3600)]" - }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'roleAssignments')]" - } - }, - "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.32.4.45862", - "templateHash": "1855369119498044639" - }, - "name": "Private DNS Zone TXT record", - "description": "This module deploys a Private DNS Zone TXT record.", - "owner": "Azure/module-maintainers" - }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } - } - } - }, - "nullable": true - } - }, - "parameters": { - "privateDnsZoneName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the TXT record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata attached to the record set." - } - }, - "ttl": { - "type": "int", - "defaultValue": 3600, - "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." - } - }, - "txtRecords": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. The list of TXT records in the record set." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - } - }, - "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')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", - "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" - } - }, - "resources": { - "privateDnsZone": { - "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" - }, - "TXT": { - "type": "Microsoft.Network/privateDnsZones/TXT", - "apiVersion": "2020-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "properties": { - "metadata": "[parameters('metadata')]", - "ttl": "[parameters('ttl')]", - "txtRecords": "[parameters('txtRecords')]" - } - }, - "TXT_roleAssignments": { - "copy": { - "name": "TXT_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/TXT/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/TXT', parameters('privateDnsZoneName'), 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": [ - "TXT" - ] - } - }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the deployed TXT record." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the deployed TXT record." - }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/TXT', parameters('privateDnsZoneName'), parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group of the deployed TXT record." - }, - "value": "[resourceGroup().name]" - } - } - } - }, - "dependsOn": [ - "privateDnsZone" - ] - }, - "privateDnsZone_virtualNetworkLinks": { - "copy": { - "name": "privateDnsZone_virtualNetworkLinks", - "count": "[length(coalesce(parameters('virtualNetworkLinks'), createArray()))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-VirtualNetworkLink-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "privateDnsZoneName": { - "value": "[parameters('name')]" - }, - "name": { - "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'name'), format('{0}-vnetlink', last(split(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()].virtualNetworkResourceId, '/'))))]" - }, - "virtualNetworkResourceId": { - "value": "[coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()].virtualNetworkResourceId]" - }, - "location": { - "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'location'), 'global')]" - }, - "registrationEnabled": { - "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'registrationEnabled'), false())]" - }, - "tags": { - "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" - }, - "resolutionPolicy": { - "value": "[tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'resolutionPolicy')]" - } - }, - "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.32.4.45862", - "templateHash": "15326596012552051215" - }, - "name": "Private DNS Zone Virtual Network Link", - "description": "This module deploys a Private DNS Zone Virtual Network Link.", - "owner": "Azure/module-maintainers" - }, - "parameters": { - "privateDnsZoneName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", - "defaultValue": "[format('{0}-vnetlink', last(split(parameters('virtualNetworkResourceId'), '/')))]", - "metadata": { - "description": "Optional. The name of the virtual network link." - } - }, - "location": { - "type": "string", - "defaultValue": "global", - "metadata": { - "description": "Optional. The location of the PrivateDNSZone. Should be global." - } - }, - "tags": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Tags of the resource." - } - }, - "registrationEnabled": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "Optional. Is auto-registration of virtual machine records in the virtual network in the Private DNS zone enabled?." - } - }, - "virtualNetworkResourceId": { - "type": "string", - "metadata": { - "description": "Required. Link to another virtual network resource ID." - } - }, - "resolutionPolicy": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The resolution policy on the virtual network link. Only applicable for virtual network links to privatelink zones, and for A,AAAA,CNAME queries. When set to `NxDomainRedirect`, Azure DNS resolver falls back to public resolution if private dns query resolution results in non-existent domain response. `Default` is configured as the default option." - } - } - }, - "resources": { - "privateDnsZone": { - "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" - }, - "virtualNetworkLink": { - "type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks", - "apiVersion": "2024-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", - "properties": { - "registrationEnabled": "[parameters('registrationEnabled')]", - "virtualNetwork": { - "id": "[parameters('virtualNetworkResourceId')]" - }, - "resolutionPolicy": "[parameters('resolutionPolicy')]" - } - } - }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the deployed virtual network link." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the deployed virtual network link." - }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/virtualNetworkLinks', parameters('privateDnsZoneName'), parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group of the deployed virtual network link." - }, - "value": "[resourceGroup().name]" - }, - "location": { - "type": "string", - "metadata": { - "description": "The location the resource was deployed into." - }, - "value": "[reference('virtualNetworkLink', '2024-06-01', 'full').location]" - } - } - } - }, - "dependsOn": [ - "privateDnsZone" - ] - } - }, - "outputs": { - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group the private DNS zone was deployed into." - }, - "value": "[resourceGroup().name]" - }, - "name": { - "type": "string", - "metadata": { - "description": "The name of the private DNS zone." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the private DNS zone." - }, - "value": "[resourceId('Microsoft.Network/privateDnsZones', parameters('name'))]" - }, - "location": { - "type": "string", - "metadata": { - "description": "The location the resource was deployed into." - }, - "value": "[reference('privateDnsZone', '2020-06-01', 'full').location]" - } - } - } - } - }, - "hub": { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[take(format('{0}-ai-hub-deployment', variables('nameFormatted')), 64)]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[variables('nameFormatted')]" - }, - "sku": { - "value": "Standard" - }, - "kind": { - "value": "Hub" - }, - "description": { - "value": "[variables('nameFormatted')]" - }, - "associatedApplicationInsightsResourceId": { - "value": "[parameters('appInsightsResourceId')]" - }, - "associatedContainerRegistryResourceId": { - "value": "[parameters('containerRegistryResourceId')]" - }, - "associatedKeyVaultResourceId": { - "value": "[parameters('keyVaultResourceId')]" - }, - "associatedStorageAccountResourceId": { - "value": "[parameters('storageAccountResourceId')]" - }, - "publicNetworkAccess": "[if(parameters('networkIsolation'), createObject('value', 'Disabled'), createObject('value', 'Enabled'))]", - "managedNetworkSettings": { - "value": { - "isolationMode": "[if(parameters('networkIsolation'), 'AllowInternetOutbound', 'Disabled')]" - } - }, - "connections": { - "value": "[parameters('connections')]" - }, - "roleAssignments": { - "value": "[parameters('roleAssignments')]" - }, - "diagnosticSettings": { - "value": [ - { - "workspaceResourceId": "[parameters('logAnalyticsWorkspaceResourceId')]", - "metricCategories": [ - { - "category": "AllMetrics" - } - ], - "logCategoriesAndGroups": [ - { - "category": "ComputeInstanceEvent" - } - ] - } - ] - }, - "privateEndpoints": "[if(parameters('networkIsolation'), createObject('value', createArray(createObject('privateDnsZoneGroup', createObject('privateDnsZoneGroupConfigs', createArray(createObject('privateDnsZoneResourceId', reference('mlNotebooksPrivateDnsZone').outputs.resourceId.value), createObject('privateDnsZoneResourceId', reference('mlApiPrivateDnsZone').outputs.resourceId.value))), 'service', 'amlworkspace', 'subnetResourceId', parameters('virtualNetworkSubnetResourceId')))), createObject('value', createArray()))]", - "location": { - "value": "[parameters('location')]" - }, - "systemDatastoresAuthMode": { - "value": "identity" - }, - "tags": { - "value": "[parameters('tags')]" - } - }, - "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.33.93.31351", - "templateHash": "760529655692286424" - }, - "name": "Machine Learning Services Workspaces", - "description": "This module deploys a Machine Learning Services Workspace." - }, - "definitions": { - "featureStoreSettingType": { - "type": "object", - "properties": { - "computeRuntime": { - "type": "object", - "properties": { - "sparkRuntimeVersion": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The spark runtime version." - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. Compute runtime config for feature store type workspace." - } - }, - "offlineStoreConnectionName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The offline store connection name." - } - }, - "onlineStoreConnectionName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The online store connection name." - } - } - }, - "metadata": { - "__bicep_export!": true - } - }, - "outboundRuleType": { - "type": "object", - "discriminator": { - "propertyName": "type", - "mapping": { - "FQDN": { - "$ref": "#/definitions/fqdnoutboundRuleType" - }, - "PrivateEndpoint": { - "$ref": "#/definitions/privateEndpointoutboundRuleType" - }, - "ServiceTag": { - "$ref": "#/definitions/serviceTagoutboundRuleType" - } - } - }, - "metadata": { - "__bicep_export!": true - } - }, - "fqdnoutboundRuleType": { - "type": "object", - "properties": { - "type": { - "type": "string", - "allowedValues": [ - "FQDN" - ], - "metadata": { - "description": "Required. Type of a managed network Outbound Rule of a machine learning workspace. Only supported when 'isolationMode' is 'AllowOnlyApprovedOutbound'." - } - }, - "destination": { - "type": "string", - "metadata": { - "description": "Required. Fully Qualified Domain Name to allow for outbound traffic." - } - }, - "category": { - "type": "string", - "allowedValues": [ - "Dependency", - "Recommended", - "Required", - "UserDefined" - ], - "nullable": true, - "metadata": { - "description": "Optional. Category of a managed network Outbound Rule of a machine learning workspace." - } - } - }, - "metadata": { - "__bicep_export!": true - } - }, - "privateEndpointoutboundRuleType": { - "type": "object", - "properties": { - "type": { - "type": "string", - "allowedValues": [ - "PrivateEndpoint" - ], - "metadata": { - "description": "Required. Type of a managed network Outbound Rule of a machine learning workspace. Only supported when 'isolationMode' is 'AllowOnlyApprovedOutbound' or 'AllowInternetOutbound'." - } - }, - "destination": { - "type": "object", - "properties": { - "serviceResourceId": { - "type": "string", - "metadata": { - "description": "Required. The resource ID of the target resource for the private endpoint." - } - }, - "sparkEnabled": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Whether the private endpoint can be used by jobs running on Spark." - } - }, - "subresourceTarget": { - "type": "string", - "metadata": { - "description": "Required. The sub resource to connect for the private endpoint." - } - } - }, - "metadata": { - "description": "Required. Service Tag destination for a Service Tag Outbound Rule for the managed network of a machine learning workspace." - } - }, - "category": { - "type": "string", - "allowedValues": [ - "Dependency", - "Recommended", - "Required", - "UserDefined" - ], - "nullable": true, - "metadata": { - "description": "Optional. Category of a managed network Outbound Rule of a machine learning workspace." - } - } - }, - "metadata": { - "__bicep_export!": true - } - }, - "serviceTagoutboundRuleType": { - "type": "object", - "properties": { - "type": { - "type": "string", - "allowedValues": [ - "ServiceTag" - ], - "metadata": { - "description": "Required. Type of a managed network Outbound Rule of a machine learning workspace. Only supported when 'isolationMode' is 'AllowOnlyApprovedOutbound'." - } - }, - "destination": { - "type": "object", - "properties": { - "portRanges": { - "type": "string", - "metadata": { - "description": "Required. The name of the service tag to allow." - } - }, - "protocol": { - "type": "string", - "allowedValues": [ - "*", - "ICMP", - "TCP", - "UDP" - ], - "metadata": { - "description": "Required. The protocol to allow. Provide an asterisk(*) to allow any protocol." - } - }, - "serviceTag": { - "type": "string", - "metadata": { - "description": "Required. Which ports will be allow traffic by this rule. Provide an asterisk(*) to allow any port." - } - } - }, - "metadata": { - "description": "Required. Service Tag destination for a Service Tag Outbound Rule for the managed network of a machine learning workspace." - } - }, - "category": { - "type": "string", - "allowedValues": [ - "Dependency", - "Recommended", - "Required", - "UserDefined" - ], - "nullable": true, - "metadata": { - "description": "Optional. Category of a managed network Outbound Rule of a machine learning workspace." - } - } - }, - "metadata": { - "__bicep_export!": true - } - }, - "managedNetworkSettingType": { - "type": "object", - "properties": { - "isolationMode": { - "type": "string", - "allowedValues": [ - "AllowInternetOutbound", - "AllowOnlyApprovedOutbound", - "Disabled" - ], - "metadata": { - "description": "Required. Isolation mode for the managed network of a machine learning workspace." - } - }, - "outboundRules": { - "type": "object", - "properties": {}, - "additionalProperties": { - "$ref": "#/definitions/outboundRuleType", - "metadata": { - "description": "Required. The outbound rule. The name of the rule is the object key." - } - }, - "nullable": true, - "metadata": { - "description": "Optional. Outbound rules for the managed network of a machine learning workspace." - } - } - }, - "metadata": { - "__bicep_export!": true - } - }, - "serverlessComputeSettingType": { - "type": "object", - "properties": { - "serverlessComputeCustomSubnet": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The resource ID of an existing virtual network subnet in which serverless compute nodes should be deployed." - } - }, - "serverlessComputeNoPublicIP": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. The flag to signal if serverless compute nodes deployed in custom vNet would have no public IP addresses for a workspace with private endpoint." - } - } - }, - "metadata": { - "__bicep_export!": true - } - }, - "workspaceHubConfigType": { - "type": "object", - "properties": { - "additionalWorkspaceStorageAccounts": { - "type": "array", - "items": { - "type": "string" - }, - "nullable": true, - "metadata": { - "description": "Optional. The resource IDs of additional storage accounts to attach to the workspace." - } - }, - "defaultWorkspaceResourceGroup": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The resource ID of the default resource group for projects created in the workspace hub." - } - } - }, - "metadata": { - "__bicep_export!": true - } - }, - "connectionType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. Name of the connection to create." - } - }, - "category": { - "$ref": "#/definitions/categoryType", - "metadata": { - "description": "Required. Category of the connection." - } - }, - "expiryTime": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The expiry time of the connection." - } - }, - "isSharedToAll": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Indicates whether the connection is shared to all users in the workspace." - } - }, - "metadata": { - "type": "object", - "properties": {}, - "additionalProperties": { - "type": "string", - "metadata": { - "description": "Required. The metadata key-value pairs." - } - }, - "nullable": true, - "metadata": { - "description": "Optional. User metadata for the connection." - } - }, - "sharedUserList": { - "type": "array", - "items": { - "type": "string" - }, - "nullable": true, - "metadata": { - "description": "Optional. The shared user list of the connection." - } - }, - "target": { - "type": "string", - "metadata": { - "description": "Required. The target of the connection." - } - }, - "value": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Value details of the workspace connection." - } - }, - "connectionProperties": { - "$ref": "#/definitions/connectionPropertyType", - "metadata": { - "description": "Required. The properties of the connection, specific to the auth type." - } - } - }, - "metadata": { - "__bicep_export!": true - } - }, - "_1.privateEndpointCustomDnsConfigType": { - "type": "object", - "properties": { - "fqdn": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. FQDN that resolves to private endpoint IP address." - } - }, - "ipAddresses": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. A list of private IP addresses of the private endpoint." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" - } - } - }, - "_1.privateEndpointIpConfigurationType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the resource that is unique within a resource group." - } - }, - "properties": { - "type": "object", - "properties": { - "groupId": { - "type": "string", - "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." - } - }, - "memberName": { - "type": "string", - "metadata": { - "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to." - } - }, - "privateIPAddress": { - "type": "string", - "metadata": { - "description": "Required. A private IP address obtained from the private endpoint's subnet." - } - } - }, - "metadata": { - "description": "Required. Properties of private endpoint IP configurations." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" - } - } - }, - "_1.privateEndpointPrivateDnsZoneGroupType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the Private DNS Zone Group." - } - }, - "privateDnsZoneGroupConfigs": { - "type": "array", - "items": { - "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": "Required. The private DNS Zone Groups to associate the Private Endpoint. A DNS Zone Group can support up to 5 DNS zones." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" - } - } - }, - "_2.aadAuthTypeWorkspaceConnectionPropertyType": { - "type": "object", - "properties": { - "authType": { - "type": "string", - "allowedValues": [ - "AAD" - ], - "metadata": { - "description": "Required. The authentication type of the connection target." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "connection/main.bicep" - } - } - }, - "_2.accessKeyAuthTypeWorkspaceConnectionPropertyType": { - "type": "object", - "properties": { - "authType": { - "type": "string", - "allowedValues": [ - "AccessKey" - ], - "metadata": { - "description": "Required. The authentication type of the connection target." - } - }, - "credentials": { - "$ref": "#/definitions/_2.workspaceConnectionAccessKeyType", - "metadata": { - "description": "Required. The credentials for the connection." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "connection/main.bicep" - } - } - }, - "_2.apiKeyAuthWorkspaceConnectionPropertyType": { - "type": "object", - "properties": { - "authType": { - "type": "string", - "allowedValues": [ - "ApiKey" - ], - "metadata": { - "description": "Required. The authentication type of the connection target." - } - }, - "credentials": { - "$ref": "#/definitions/_2.workspaceConnectionApiKeyType", - "metadata": { - "description": "Required. The credentials for the connection." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "connection/main.bicep" - } - } - }, - "_2.customKeysType": { - "type": "object", - "properties": { - "keys": { - "type": "object", - "properties": {}, - "additionalProperties": { - "type": "string", - "metadata": { - "description": "Required. Key-value pairs for the custom keys." - } - }, - "metadata": { - "description": "Required. The custom keys for the connection." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "connection/main.bicep" - } - } - }, - "_2.customKeysWorkspaceConnectionPropertyType": { - "type": "object", - "properties": { - "authType": { - "type": "string", - "allowedValues": [ - "CustomKeys" - ], - "metadata": { - "description": "Required. The authentication type of the connection target." - } - }, - "credentials": { - "$ref": "#/definitions/_2.customKeysType", - "metadata": { - "description": "Required. The credentials for the connection." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "connection/main.bicep" - } - } - }, - "_2.managedIdentityAuthTypeWorkspaceConnectionPropertyType": { - "type": "object", - "properties": { - "authType": { - "type": "string", - "allowedValues": [ - "ManagedIdentity" - ], - "metadata": { - "description": "Required. The authentication type of the connection target." - } - }, - "credentials": { - "$ref": "#/definitions/_2.workspaceConnectionManagedIdentityType", - "metadata": { - "description": "Required. The credentials for the connection." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "connection/main.bicep" - } - } - }, - "_2.noneAuthTypeWorkspaceConnectionPropertyType": { - "type": "object", - "properties": { - "authType": { - "type": "string", - "allowedValues": [ - "None" - ], - "metadata": { - "description": "Required. The authentication type of the connection target." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "connection/main.bicep" - } - } - }, - "_2.oauth2AuthTypeWorkspaceConnectionPropertyType": { - "type": "object", - "properties": { - "authType": { - "type": "string", - "allowedValues": [ - "OAuth2" - ], - "metadata": { - "description": "Required. The authentication type of the connection target." - } - }, - "credentials": { - "$ref": "#/definitions/_2.workspaceConnectionOAuth2Type", - "metadata": { - "description": "Required. The credentials for the connection." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "connection/main.bicep" - } - } - }, - "_2.patAuthTypeWorkspaceConnectionPropertyType": { - "type": "object", - "properties": { - "authType": { - "type": "string", - "allowedValues": [ - "PAT" - ], - "metadata": { - "description": "Required. The authentication type of the connection target." - } - }, - "credentials": { - "$ref": "#/definitions/_2.workspaceConnectionPersonalAccessTokenType", - "metadata": { - "description": "Required. The credentials for the connection." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "connection/main.bicep" - } - } - }, - "_2.sasAuthTypeWorkspaceConnectionPropertyType": { - "type": "object", - "properties": { - "authType": { - "type": "string", - "allowedValues": [ - "SAS" - ], - "metadata": { - "description": "Required. The authentication type of the connection target." - } - }, - "credentials": { - "$ref": "#/definitions/_2.workspaceConnectionSharedAccessSignatureType", - "metadata": { - "description": "Required. The credentials for the connection." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "connection/main.bicep" - } - } - }, - "_2.servicePrincipalAuthTypeWorkspaceConnectionPropertyType": { - "type": "object", - "properties": { - "authType": { - "type": "string", - "allowedValues": [ - "ServicePrincipal" - ], - "metadata": { - "description": "Required. The authentication type of the connection target." - } - }, - "credentials": { - "$ref": "#/definitions/_2.workspaceConnectionServicePrincipalType", - "metadata": { - "description": "Required. The credentials for the connection." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "connection/main.bicep" - } - } - }, - "_2.usernamePasswordAuthTypeWorkspaceConnectionPropertyType": { - "type": "object", - "properties": { - "authType": { - "type": "string", - "allowedValues": [ - "UsernamePassword" - ], - "metadata": { - "description": "Required. The authentication type of the connection target." - } - }, - "credentials": { - "$ref": "#/definitions/_2.workspaceConnectionUsernamePasswordType", - "metadata": { - "description": "Required. The credentials for the connection." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "connection/main.bicep" - } - } - }, - "_2.workspaceConnectionAccessKeyType": { - "type": "object", - "properties": { - "accessKeyId": { - "type": "string", - "metadata": { - "description": "Required. The connection access key ID." - } - }, - "secretAccessKey": { - "type": "string", - "metadata": { - "description": "Required. The connection secret access key." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "connection/main.bicep" - } - } - }, - "_2.workspaceConnectionApiKeyType": { - "type": "object", - "properties": { - "key": { - "type": "string", - "metadata": { - "description": "Required. The connection API key." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "connection/main.bicep" - } - } - }, - "_2.workspaceConnectionManagedIdentityType": { - "type": "object", - "properties": { - "clientId": { - "type": "string", - "metadata": { - "description": "Required. The connection managed identity ID." - } - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "Required. The connection managed identity resource ID." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "connection/main.bicep" - } - } - }, - "_2.workspaceConnectionOAuth2Type": { - "type": "object", - "properties": { - "authUrl": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Conditional. The connection auth URL. Required by Concur connection category." - } - }, - "clientId": { - "type": "string", - "minLength": 36, - "maxLength": 36, - "metadata": { - "description": "Required. The connection client ID in the format of UUID." - } - }, - "clientSecret": { - "type": "string", - "metadata": { - "description": "Required. The connection client secret." - } - }, - "developerToken": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Conditional. The connection developer token. Required by GoogleAdWords connection category." - } - }, - "password": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Conditional. The connection password. Required by Concur and ServiceNow connection categories where AccessToken grant type is 'Password'." - } - }, - "refreshToken": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Conditional. The connection refresh token. Required by GoogleBigQuery, GoogleAdWords, Hubspot, QuickBooks, Square, Xero and Zoho connection categories." - } - }, - "tenantId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Required. The connection tenant ID. Required by QuickBooks and Xero connection categories." - } - }, - "username": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Conditional. The connection username. Required by Concur and ServiceNow connection categories where AccessToken grant type is 'Password'." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "connection/main.bicep" - } - } - }, - "_2.workspaceConnectionPersonalAccessTokenType": { - "type": "object", - "properties": { - "pat": { - "type": "string", - "metadata": { - "description": "Required. The connection personal access token." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "connection/main.bicep" - } - } - }, - "_2.workspaceConnectionServicePrincipalType": { - "type": "object", - "properties": { - "clientId": { - "type": "string", - "metadata": { - "description": "Required. The connection client ID." - } - }, - "clientSecret": { - "type": "string", - "metadata": { - "description": "Required. The connection client secret." - } - }, - "tenantId": { - "type": "string", - "metadata": { - "description": "Required. The connection tenant ID." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "connection/main.bicep" - } - } - }, - "_2.workspaceConnectionSharedAccessSignatureType": { - "type": "object", - "properties": { - "sas": { - "type": "string", - "metadata": { - "description": "Required. The connection SAS token." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "connection/main.bicep" - } - } - }, - "_2.workspaceConnectionUsernamePasswordType": { - "type": "object", - "properties": { - "password": { - "type": "string", - "metadata": { - "description": "Required. The connection password." - } - }, - "securityToken": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Conditional. The connection security token. Required by connections like SalesForce for extra security in addition to 'UsernamePassword'." - } - }, - "username": { - "type": "string", - "metadata": { - "description": "Required. The connection username." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "connection/main.bicep" - } - } - }, - "categoryType": { - "type": "string", - "allowedValues": [ - "ADLSGen2", - "AIServices", - "AmazonMws", - "AmazonRdsForOracle", - "AmazonRdsForSqlServer", - "AmazonRedshift", - "AmazonS3Compatible", - "ApiKey", - "AzureBlob", - "AzureDataExplorer", - "AzureDatabricksDeltaLake", - "AzureMariaDb", - "AzureMySqlDb", - "AzureOneLake", - "AzureOpenAI", - "AzurePostgresDb", - "AzureSqlDb", - "AzureSqlMi", - "AzureSynapseAnalytics", - "AzureTableStorage", - "BingLLMSearch", - "Cassandra", - "CognitiveSearch", - "CognitiveService", - "Concur", - "ContainerRegistry", - "CosmosDb", - "CosmosDbMongoDbApi", - "Couchbase", - "CustomKeys", - "Db2", - "Drill", - "Dynamics", - "DynamicsAx", - "DynamicsCrm", - "Eloqua", - "FileServer", - "FtpServer", - "GenericContainerRegistry", - "GenericHttp", - "GenericRest", - "Git", - "GoogleAdWords", - "GoogleBigQuery", - "GoogleCloudStorage", - "Greenplum", - "Hbase", - "Hdfs", - "Hive", - "Hubspot", - "Impala", - "Informix", - "Jira", - "Magento", - "MariaDb", - "Marketo", - "MicrosoftAccess", - "MongoDbAtlas", - "MongoDbV2", - "MySql", - "Netezza", - "ODataRest", - "Odbc", - "Office365", - "OpenAI", - "Oracle", - "OracleCloudStorage", - "OracleServiceCloud", - "PayPal", - "Phoenix", - "PostgreSql", - "Presto", - "PythonFeed", - "QuickBooks", - "Redis", - "Responsys", - "S3", - "Salesforce", - "SalesforceMarketingCloud", - "SalesforceServiceCloud", - "SapBw", - "SapCloudForCustomer", - "SapEcc", - "SapHana", - "SapOpenHub", - "SapTable", - "Serp", - "Serverless", - "ServiceNow", - "Sftp", - "SharePointOnlineList", - "Shopify", - "Snowflake", - "Spark", - "SqlServer", - "Square", - "Sybase", - "Teradata", - "Vertica", - "WebTable", - "Xero", - "Zoho" - ], - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "connection/main.bicep" - } - } - }, - "connectionPropertyType": { - "type": "secureObject", - "discriminator": { - "propertyName": "authType", - "mapping": { - "AAD": { - "$ref": "#/definitions/_2.aadAuthTypeWorkspaceConnectionPropertyType" - }, - "AccessKey": { - "$ref": "#/definitions/_2.accessKeyAuthTypeWorkspaceConnectionPropertyType" - }, - "ApiKey": { - "$ref": "#/definitions/_2.apiKeyAuthWorkspaceConnectionPropertyType" - }, - "CustomKeys": { - "$ref": "#/definitions/_2.customKeysWorkspaceConnectionPropertyType" - }, - "ManagedIdentity": { - "$ref": "#/definitions/_2.managedIdentityAuthTypeWorkspaceConnectionPropertyType" - }, - "None": { - "$ref": "#/definitions/_2.noneAuthTypeWorkspaceConnectionPropertyType" - }, - "OAuth2": { - "$ref": "#/definitions/_2.oauth2AuthTypeWorkspaceConnectionPropertyType" - }, - "PAT": { - "$ref": "#/definitions/_2.patAuthTypeWorkspaceConnectionPropertyType" - }, - "SAS": { - "$ref": "#/definitions/_2.sasAuthTypeWorkspaceConnectionPropertyType" - }, - "ServicePrincipal": { - "$ref": "#/definitions/_2.servicePrincipalAuthTypeWorkspaceConnectionPropertyType" - }, - "UsernamePassword": { - "$ref": "#/definitions/_2.usernamePasswordAuthTypeWorkspaceConnectionPropertyType" - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "connection/main.bicep" - } - } - }, - "customerManagedKeyType": { - "type": "object", - "properties": { - "keyVaultResourceId": { - "type": "string", - "metadata": { - "description": "Required. The resource ID of a key vault to reference a customer managed key for encryption from." - } - }, - "keyName": { - "type": "string", - "metadata": { - "description": "Required. The name of the customer managed key to use for encryption." - } - }, - "keyVersion": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The version of the customer managed key to reference for encryption. If not provided, the deployment will use the latest version available at deployment time." - } - }, - "userAssignedIdentityResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use." - } - } - }, - "metadata": { - "description": "An AVM-aligned type for a customer-managed key. To be used if the resource type does not support auto-rotation of the customer-managed key.", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" - } - } - }, - "diagnosticSettingFullType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the diagnostic setting." - } - }, - "logCategoriesAndGroups": { - "type": "array", - "items": { - "type": "object", - "properties": { - "category": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." - } - }, - "categoryGroup": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." - } - }, - "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 logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." - } - }, - "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 both logs & metrics are supported by the resource provider.", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.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." - } - } - }, - "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.4.0" - } - } - }, - "managedIdentityAllType": { - "type": "object", - "properties": { - "systemAssigned": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Enables system assigned managed identity on the resource." - } - }, - "userAssignedResourceIds": { - "type": "array", - "items": { - "type": "string" - }, - "nullable": true, - "metadata": { - "description": "Optional. The resource ID(s) to assign to the resource. Required if a user assigned identity is used for encryption." - } - } - }, - "metadata": { - "description": "An AVM-aligned type for a managed identity configuration. To be used if both a system-assigned & user-assigned identities are supported by the resource provider.", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" - } - } - }, - "privateEndpointSingleServiceType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the Private Endpoint." - } - }, - "location": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The location to deploy the Private Endpoint to." - } - }, - "privateLinkServiceConnectionName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the private link connection to create." - } - }, - "service": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The subresource to deploy the Private Endpoint for. For example \"vault\" for a Key Vault Private Endpoint." - } - }, - "subnetResourceId": { - "type": "string", - "metadata": { - "description": "Required. Resource ID of the subnet where the endpoint needs to be created." - } - }, - "privateDnsZoneGroup": { - "$ref": "#/definitions/_1.privateEndpointPrivateDnsZoneGroupType", - "nullable": true, - "metadata": { - "description": "Optional. The private DNS Zone Group to configure for the Private Endpoint." - } - }, - "isManualConnection": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. If Manual Private Link Connection is required." - } - }, - "manualConnectionRequestMessage": { - "type": "string", - "nullable": true, - "maxLength": 140, - "metadata": { - "description": "Optional. A message passed to the owner of the remote resource with the manual connection request." - } - }, - "customDnsConfigs": { - "type": "array", - "items": { - "$ref": "#/definitions/_1.privateEndpointCustomDnsConfigType" - }, - "nullable": true, - "metadata": { - "description": "Optional. Custom DNS configurations." - } - }, - "ipConfigurations": { - "type": "array", - "items": { - "$ref": "#/definitions/_1.privateEndpointIpConfigurationType" - }, - "nullable": true, - "metadata": { - "description": "Optional. A list of IP configurations of the Private Endpoint. This will be used to map to the first-party Service endpoints." - } - }, - "applicationSecurityGroupResourceIds": { - "type": "array", - "items": { - "type": "string" - }, - "nullable": true, - "metadata": { - "description": "Optional. Application security groups in which the Private Endpoint IP configuration is included." - } - }, - "customNetworkInterfaceName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The custom name of the network interface attached to the Private Endpoint." - } - }, - "lock": { - "$ref": "#/definitions/lockType", - "nullable": true, - "metadata": { - "description": "Optional. Specify the type of lock." - } - }, - "roleAssignments": { - "type": "array", - "items": { - "$ref": "#/definitions/roleAssignmentType" - }, - "nullable": true, - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "tags": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Tags to be applied on all resources/Resource Groups in this deployment." - } - }, - "enableTelemetry": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Enable/Disable usage telemetry for module." - } - }, - "resourceGroupName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Specify if you want to deploy the Private Endpoint into a different Resource Group than the main resource." - } - } - }, - "metadata": { - "description": "An AVM-aligned type for a private endpoint. To be used if the private endpoint's default service / groupId can be assumed (i.e., for services that only have one Private Endpoint type like 'vault' for key vault).", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.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.4.0" - } - } - } - }, - "parameters": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the machine learning workspace." - } - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]", - "metadata": { - "description": "Optional. Location for all resources." - } - }, - "sku": { - "type": "string", - "allowedValues": [ - "Free", - "Basic", - "Standard", - "Premium" - ], - "metadata": { - "description": "Required. Specifies the SKU, also referred as 'edition' of the Azure Machine Learning workspace." - } - }, - "kind": { - "type": "string", - "defaultValue": "Default", - "allowedValues": [ - "Default", - "Project", - "Hub", - "FeatureStore" - ], - "metadata": { - "description": "Optional. The type of Azure Machine Learning workspace to create." - } - }, - "associatedStorageAccountResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Conditional. The resource ID of the associated Storage Account. Required if 'kind' is 'Default', 'FeatureStore' or 'Hub'." - } - }, - "associatedKeyVaultResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Conditional. The resource ID of the associated Key Vault. Required if 'kind' is 'Default', 'FeatureStore' or 'Hub'." - } - }, - "associatedApplicationInsightsResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Conditional. The resource ID of the associated Application Insights. Required if 'kind' is 'Default' or 'FeatureStore'." - } - }, - "associatedContainerRegistryResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The resource ID of the associated Container Registry." - } - }, - "lock": { - "$ref": "#/definitions/lockType", - "nullable": true, - "metadata": { - "description": "Optional. The lock settings of the service." - } - }, - "hbiWorkspace": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "Optional. The flag to signal HBI data in the workspace and reduce diagnostic data collected by the service." - } - }, - "hubResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Conditional. The resource ID of the hub to associate with the workspace. Required if 'kind' is set to 'Project'." - } - }, - "roleAssignments": { - "type": "array", - "items": { - "$ref": "#/definitions/roleAssignmentType" - }, - "nullable": true, - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "privateEndpoints": { - "type": "array", - "items": { - "$ref": "#/definitions/privateEndpointSingleServiceType" - }, - "nullable": true, - "metadata": { - "description": "Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible." - } - }, - "computes": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. Computes to create respectively attach to the workspace." - } - }, - "connections": { - "type": "array", - "items": { - "$ref": "#/definitions/connectionType" - }, - "defaultValue": [], - "metadata": { - "description": "Optional. Connections to create in the workspace." - } - }, - "tags": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Resource tags." - } - }, - "enableTelemetry": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Optional. Enable/Disable usage telemetry for module." - } - }, - "managedIdentities": { - "$ref": "#/definitions/managedIdentityAllType", - "defaultValue": { - "systemAssigned": true - }, - "metadata": { - "description": "Optional. The managed identity definition for this resource. At least one identity type is required." - } - }, - "featureStoreSettings": { - "$ref": "#/definitions/featureStoreSettingType", - "nullable": true, - "metadata": { - "description": "Conditional. Settings for feature store type workspaces. Required if 'kind' is set to 'FeatureStore'." - } - }, - "managedNetworkSettings": { - "$ref": "#/definitions/managedNetworkSettingType", - "nullable": true, - "metadata": { - "description": "Optional. Managed Network settings for a machine learning workspace." - } - }, - "serverlessComputeSettings": { - "$ref": "#/definitions/serverlessComputeSettingType", - "nullable": true, - "metadata": { - "description": "Optional. Settings for serverless compute created in the workspace." - } - }, - "systemDatastoresAuthMode": { - "type": "string", - "nullable": true, - "allowedValues": [ - "accessKey", - "identity" - ], - "metadata": { - "description": "Optional. The authentication mode used by the workspace when connecting to the default storage account." - } - }, - "workspaceHubConfig": { - "$ref": "#/definitions/workspaceHubConfigType", - "nullable": true, - "metadata": { - "description": "Optional. Configuration for workspace hub settings." - } - }, - "diagnosticSettings": { - "type": "array", - "items": { - "$ref": "#/definitions/diagnosticSettingFullType" - }, - "nullable": true, - "metadata": { - "description": "Optional. The diagnostic settings of the service." - } - }, - "description": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The description of this workspace." - } - }, - "discoveryUrl": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. URL for the discovery service to identify regional endpoints for machine learning experimentation services." - } - }, - "customerManagedKey": { - "$ref": "#/definitions/customerManagedKeyType", - "nullable": true, - "metadata": { - "description": "Optional. The customer managed key definition." - } - }, - "imageBuildCompute": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The compute name for image build." - } - }, - "primaryUserAssignedIdentity": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Conditional. The user assigned identity resource ID that represents the workspace identity. Required if 'userAssignedIdentities' is not empty and may not be used if 'systemAssignedIdentity' is enabled." - } - }, - "serviceManagedResourcesSettings": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The service managed resource settings." - } - }, - "sharedPrivateLinkResources": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. The list of shared private link resources in this workspace. Note: This property is not idempotent." - } - }, - "publicNetworkAccess": { - "type": "string", - "defaultValue": "Disabled", - "allowedValues": [ - "Enabled", - "Disabled" - ], - "metadata": { - "description": "Optional. Whether or not public network access is allowed for this resource. For security reasons it should be disabled." - } - } - }, - "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)))))]" - } - ], - "enableReferencedModulesTelemetry": false, - "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", - "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned,UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', 'None')), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", - "builtInRoleNames": { - "AzureML Compute Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e503ece1-11d0-4e8e-8e2c-7a6c3bf38815')]", - "AzureML Data Scientist": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f6c7c914-8db3-469d-8ca1-694a8f32e121')]", - "AzureML Metrics Writer (preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '635dd51f-9968-44d3-b7fb-6d9a6bd613ae')]", - "AzureML Registry User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1823dd4f-9b8c-4ab6-ab4e-7397a3684615')]", - "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" - } - }, - "resources": { - "cMKKeyVault::cMKKey": { - "condition": "[and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), not(empty(tryGet(parameters('customerManagedKey'), 'keyName')))))]", - "existing": true, - "type": "Microsoft.KeyVault/vaults/keys", - "apiVersion": "2023-02-01", - "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '//'), '/')[2]]", - "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '////'), '/')[4]]", - "name": "[format('{0}/{1}', last(split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), 'dummyVault'), '/')), coalesce(tryGet(parameters('customerManagedKey'), 'keyName'), 'dummyKey'))]" - }, - "avmTelemetry": { - "condition": "[parameters('enableTelemetry')]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2024-03-01", - "name": "[format('46d3xbcp.res.machinelearningservices-workspace.{0}.{1}', replace('0.10.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" - } - } - } - } - }, - "cMKKeyVault": { - "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId')))]", - "existing": true, - "type": "Microsoft.KeyVault/vaults", - "apiVersion": "2023-02-01", - "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '//'), '/')[2]]", - "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '////'), '/')[4]]", - "name": "[last(split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), 'dummyVault'), '/'))]" - }, - "cMKUserAssignedIdentity": { - "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId')))]", - "existing": true, - "type": "Microsoft.ManagedIdentity/userAssignedIdentities", - "apiVersion": "2023-01-31", - "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '//'), '/')[2]]", - "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '////'), '/')[4]]", - "name": "[last(split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), 'dummyMsi'), '/'))]" - }, - "workspace": { - "type": "Microsoft.MachineLearningServices/workspaces", - "apiVersion": "2024-04-01-preview", - "name": "[parameters('name')]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", - "sku": { - "name": "[parameters('sku')]", - "tier": "[parameters('sku')]" - }, - "identity": "[variables('identity')]", - "properties": "[shallowMerge(createArray(createObject('friendlyName', parameters('name'), 'storageAccount', parameters('associatedStorageAccountResourceId'), 'keyVault', parameters('associatedKeyVaultResourceId'), 'applicationInsights', parameters('associatedApplicationInsightsResourceId'), 'containerRegistry', parameters('associatedContainerRegistryResourceId'), 'hbiWorkspace', parameters('hbiWorkspace'), 'description', parameters('description'), 'discoveryUrl', parameters('discoveryUrl'), 'encryption', if(not(empty(parameters('customerManagedKey'))), createObject('status', 'Enabled', 'identity', if(not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'))), createObject('userAssignedIdentity', extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '//'), '/')[2], split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '////'), '/')[4]), 'Microsoft.ManagedIdentity/userAssignedIdentities', last(split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), 'dummyMsi'), '/')))), null()), 'keyVaultProperties', createObject('keyVaultArmId', extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '//'), '/')[2], split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '////'), '/')[4]), 'Microsoft.KeyVault/vaults', last(split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), 'dummyVault'), '/'))), 'keyIdentifier', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'keyVersion'), ''))), format('{0}/{1}', reference('cMKKeyVault::cMKKey').keyUri, parameters('customerManagedKey').keyVersion), reference('cMKKeyVault::cMKKey').keyUriWithVersion))), null()), 'imageBuildCompute', parameters('imageBuildCompute'), 'primaryUserAssignedIdentity', parameters('primaryUserAssignedIdentity'), 'systemDatastoresAuthMode', parameters('systemDatastoresAuthMode'), 'publicNetworkAccess', parameters('publicNetworkAccess'), 'serviceManagedResourcesSettings', parameters('serviceManagedResourcesSettings'), 'featureStoreSettings', parameters('featureStoreSettings'), 'hubResourceId', parameters('hubResourceId'), 'managedNetwork', parameters('managedNetworkSettings'), 'serverlessComputeSettings', parameters('serverlessComputeSettings'), 'workspaceHubConfig', parameters('workspaceHubConfig')), if(not(empty(parameters('sharedPrivateLinkResources'))), createObject('sharedPrivateLinkResources', parameters('sharedPrivateLinkResources')), createObject())))]", - "kind": "[parameters('kind')]", - "dependsOn": [ - "cMKKeyVault::cMKKey" - ] - }, - "workspace_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.MachineLearningServices/workspaces/{0}', parameters('name'))]", - "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", - "properties": { - "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", - "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" - }, - "dependsOn": [ - "workspace" - ] - }, - "workspace_diagnosticSettings": { - "copy": { - "name": "workspace_diagnosticSettings", - "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" - }, - "type": "Microsoft.Insights/diagnosticSettings", - "apiVersion": "2021-05-01-preview", - "scope": "[format('Microsoft.MachineLearningServices/workspaces/{0}', parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", - "properties": { - "copy": [ - { - "name": "metrics", - "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", - "input": { - "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", - "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", - "timeGrain": null - } - }, - { - "name": "logs", - "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", - "input": { - "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", - "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", - "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" - } - } - ], - "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", - "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", - "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", - "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", - "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", - "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" - }, - "dependsOn": [ - "workspace" - ] - }, - "workspace_roleAssignments": { - "copy": { - "name": "workspace_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.MachineLearningServices/workspaces/{0}', parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.MachineLearningServices/workspaces', 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": [ - "workspace" - ] - }, - "workspace_computes": { - "copy": { - "name": "workspace_computes", - "count": "[length(coalesce(parameters('computes'), createArray()))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-{1}-compute', parameters('name'), coalesce(parameters('computes'), createArray())[copyIndex()].name)]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "machineLearningWorkspaceName": { - "value": "[parameters('name')]" - }, - "name": { - "value": "[coalesce(parameters('computes'), createArray())[copyIndex()].name]" - }, - "location": { - "value": "[coalesce(parameters('computes'), createArray())[copyIndex()].location]" - }, - "sku": { - "value": "[tryGet(coalesce(parameters('computes'), createArray())[copyIndex()], 'sku')]" - }, - "managedIdentities": { - "value": "[tryGet(coalesce(parameters('computes'), createArray())[copyIndex()], 'managedIdentities')]" - }, - "tags": { - "value": "[tryGet(coalesce(parameters('computes'), createArray())[copyIndex()], 'tags')]" - }, - "deployCompute": { - "value": "[tryGet(coalesce(parameters('computes'), createArray())[copyIndex()], 'deployCompute')]" - }, - "computeLocation": { - "value": "[tryGet(coalesce(parameters('computes'), createArray())[copyIndex()], 'computeLocation')]" - }, - "description": { - "value": "[tryGet(coalesce(parameters('computes'), createArray())[copyIndex()], 'description')]" - }, - "disableLocalAuth": { - "value": "[tryGet(coalesce(parameters('computes'), createArray())[copyIndex()], 'disableLocalAuth')]" - }, - "resourceId": { - "value": "[tryGet(coalesce(parameters('computes'), createArray())[copyIndex()], 'resourceId')]" - }, - "computeType": { - "value": "[coalesce(parameters('computes'), createArray())[copyIndex()].computeType]" - }, - "properties": { - "value": "[tryGet(coalesce(parameters('computes'), createArray())[copyIndex()], 'properties')]" - } - }, - "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.33.93.31351", - "templateHash": "6400587070695625658" - }, - "name": "Machine Learning Services Workspaces Computes", - "description": "This module deploys a Machine Learning Services Workspaces Compute.\n\nAttaching a compute is not idempotent and will fail in case you try to redeploy over an existing compute in AML (see parameter `deployCompute`)." - }, - "definitions": { - "managedIdentityAllType": { - "type": "object", - "properties": { - "systemAssigned": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Enables system assigned managed identity on the resource." - } - }, - "userAssignedResourceIds": { - "type": "array", - "items": { - "type": "string" - }, - "nullable": true, - "metadata": { - "description": "Optional. The resource ID(s) to assign to the resource. Required if a user assigned identity is used for encryption." - } - } - }, - "metadata": { - "description": "An AVM-aligned type for a managed identity configuration. To be used if both a system-assigned & user-assigned identities are supported by the resource provider.", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" - } - } - } - }, - "parameters": { - "machineLearningWorkspaceName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent Machine Learning Workspace. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", - "minLength": 2, - "maxLength": 16, - "metadata": { - "description": "Required. Name of the compute." - } - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]", - "metadata": { - "description": "Optional. Specifies the location of the resource." - } - }, - "sku": { - "type": "string", - "nullable": true, - "allowedValues": [ - "Basic", - "Free", - "Premium", - "Standard" - ], - "metadata": { - "description": "Optional. Specifies the sku, also referred as \"edition\". Required for creating a compute resource." - } - }, - "tags": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Contains resource tags defined as key-value pairs. Ignored when attaching a compute resource, i.e. when you provide a resource ID." - } - }, - "deployCompute": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Optional. Flag to specify whether to deploy the compute. Required only for attach (i.e. providing a resource ID), as in this case the operation is not idempotent, i.e. a second deployment will fail. Therefore, this flag needs to be set to \"false\" as long as the compute resource exists." - } - }, - "computeLocation": { - "type": "string", - "defaultValue": "[resourceGroup().location]", - "metadata": { - "description": "Optional. Location for the underlying compute. Ignored when attaching a compute resource, i.e. when you provide a resource ID." - } - }, - "description": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The description of the Machine Learning compute." - } - }, - "disableLocalAuth": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "Optional. Opt-out of local authentication and ensure customers can use only MSI and AAD exclusively for authentication." - } - }, - "resourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. ARM resource ID of the underlying compute." - } - }, - "computeType": { - "type": "string", - "allowedValues": [ - "AKS", - "AmlCompute", - "ComputeInstance", - "Databricks", - "DataFactory", - "DataLakeAnalytics", - "HDInsight", - "Kubernetes", - "SynapseSpark", - "VirtualMachine" - ], - "metadata": { - "description": "Required. Set the object type." - } - }, - "properties": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The properties of the compute. Will be ignored in case \"resourceId\" is set." - } - }, - "managedIdentities": { - "$ref": "#/definitions/managedIdentityAllType", - "nullable": true, - "metadata": { - "description": "Optional. The managed identity definition for this resource." - } - } - }, - "variables": { - "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", - "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned,UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', null())), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]" - }, - "resources": { - "machineLearningWorkspace": { - "existing": true, - "type": "Microsoft.MachineLearningServices/workspaces", - "apiVersion": "2022-10-01", - "name": "[parameters('machineLearningWorkspaceName')]" - }, - "compute": { - "condition": "[equals(parameters('deployCompute'), true())]", - "type": "Microsoft.MachineLearningServices/workspaces/computes", - "apiVersion": "2022-10-01", - "name": "[format('{0}/{1}', parameters('machineLearningWorkspaceName'), parameters('name'))]", - "location": "[parameters('location')]", - "tags": "[if(empty(parameters('resourceId')), parameters('tags'), null())]", - "sku": "[if(empty(parameters('resourceId')), createObject('name', parameters('sku'), 'tier', parameters('sku')), null())]", - "identity": "[if(empty(parameters('resourceId')), variables('identity'), null())]", - "properties": "[union(createObject('description', parameters('description'), 'disableLocalAuth', parameters('disableLocalAuth'), 'computeType', parameters('computeType')), if(not(empty(parameters('resourceId'))), createObject('resourceId', parameters('resourceId')), createObject('computeLocation', parameters('computeLocation'), 'properties', parameters('properties'))))]" - } - }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the compute." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the compute." - }, - "value": "[resourceId('Microsoft.MachineLearningServices/workspaces/computes', parameters('machineLearningWorkspaceName'), parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group the compute was deployed into." - }, - "value": "[resourceGroup().name]" - }, - "systemAssignedMIPrincipalId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "The principal ID of the system assigned identity." - }, - "value": "[tryGet(tryGet(reference('compute', '2022-10-01', 'full'), 'identity'), 'principalId')]" - }, - "location": { - "type": "string", - "metadata": { - "description": "The location the resource was deployed into." - }, - "value": "[reference('compute', '2022-10-01', 'full').location]" - } - } - } - }, - "dependsOn": [ - "workspace", - "workspace_privateEndpoints" - ] - }, - "workspace_connections": { - "copy": { - "name": "workspace_connections", - "count": "[length(parameters('connections'))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-{1}-connection', parameters('name'), parameters('connections')[copyIndex()].name)]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "machineLearningWorkspaceName": { - "value": "[parameters('name')]" - }, - "name": { - "value": "[parameters('connections')[copyIndex()].name]" - }, - "category": { - "value": "[parameters('connections')[copyIndex()].category]" - }, - "expiryTime": { - "value": "[tryGet(parameters('connections')[copyIndex()], 'expiryTime')]" - }, - "isSharedToAll": { - "value": "[tryGet(parameters('connections')[copyIndex()], 'isSharedToAll')]" - }, - "metadata": { - "value": "[tryGet(parameters('connections')[copyIndex()], 'metadata')]" - }, - "sharedUserList": { - "value": "[tryGet(parameters('connections')[copyIndex()], 'sharedUserList')]" - }, - "target": { - "value": "[parameters('connections')[copyIndex()].target]" - }, - "value": { - "value": "[tryGet(parameters('connections')[copyIndex()], 'value')]" - }, - "connectionProperties": { - "value": "[parameters('connections')[copyIndex()].connectionProperties]" - } - }, - "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.33.93.31351", - "templateHash": "10473859406464148345" - }, - "name": "Machine Learning Services Workspaces Connections", - "description": "This module creates a connection in a Machine Learning Services workspace." - }, - "definitions": { - "metadataType": { - "type": "object", - "properties": {}, - "additionalProperties": { - "type": "string", - "metadata": { - "description": "Required. The metadata key-value pairs." - } - } - }, - "categoryType": { - "type": "string", - "allowedValues": [ - "ADLSGen2", - "AIServices", - "AmazonMws", - "AmazonRdsForOracle", - "AmazonRdsForSqlServer", - "AmazonRedshift", - "AmazonS3Compatible", - "ApiKey", - "AzureBlob", - "AzureDataExplorer", - "AzureDatabricksDeltaLake", - "AzureMariaDb", - "AzureMySqlDb", - "AzureOneLake", - "AzureOpenAI", - "AzurePostgresDb", - "AzureSqlDb", - "AzureSqlMi", - "AzureSynapseAnalytics", - "AzureTableStorage", - "BingLLMSearch", - "Cassandra", - "CognitiveSearch", - "CognitiveService", - "Concur", - "ContainerRegistry", - "CosmosDb", - "CosmosDbMongoDbApi", - "Couchbase", - "CustomKeys", - "Db2", - "Drill", - "Dynamics", - "DynamicsAx", - "DynamicsCrm", - "Eloqua", - "FileServer", - "FtpServer", - "GenericContainerRegistry", - "GenericHttp", - "GenericRest", - "Git", - "GoogleAdWords", - "GoogleBigQuery", - "GoogleCloudStorage", - "Greenplum", - "Hbase", - "Hdfs", - "Hive", - "Hubspot", - "Impala", - "Informix", - "Jira", - "Magento", - "MariaDb", - "Marketo", - "MicrosoftAccess", - "MongoDbAtlas", - "MongoDbV2", - "MySql", - "Netezza", - "ODataRest", - "Odbc", - "Office365", - "OpenAI", - "Oracle", - "OracleCloudStorage", - "OracleServiceCloud", - "PayPal", - "Phoenix", - "PostgreSql", - "Presto", - "PythonFeed", - "QuickBooks", - "Redis", - "Responsys", - "S3", - "Salesforce", - "SalesforceMarketingCloud", - "SalesforceServiceCloud", - "SapBw", - "SapCloudForCustomer", - "SapEcc", - "SapHana", - "SapOpenHub", - "SapTable", - "Serp", - "Serverless", - "ServiceNow", - "Sftp", - "SharePointOnlineList", - "Shopify", - "Snowflake", - "Spark", - "SqlServer", - "Square", - "Sybase", - "Teradata", - "Vertica", - "WebTable", - "Xero", - "Zoho" - ], - "metadata": { - "__bicep_export!": true - } - }, - "aadAuthTypeWorkspaceConnectionPropertyType": { - "type": "object", - "properties": { - "authType": { - "type": "string", - "allowedValues": [ - "AAD" - ], - "metadata": { - "description": "Required. The authentication type of the connection target." - } - } - } - }, - "accessKeyAuthTypeWorkspaceConnectionPropertyType": { - "type": "object", - "properties": { - "authType": { - "type": "string", - "allowedValues": [ - "AccessKey" - ], - "metadata": { - "description": "Required. The authentication type of the connection target." - } - }, - "credentials": { - "$ref": "#/definitions/workspaceConnectionAccessKeyType", - "metadata": { - "description": "Required. The credentials for the connection." - } - } - } - }, - "accountKeyAuthTypeWorkspaceConnectionPropertyType": { - "type": "object", - "properties": { - "authType": { - "type": "string", - "allowedValues": [ - "AccountKey" - ], - "metadata": { - "description": "Required. The authentication type of the connection target." - } - }, - "credentials": { - "$ref": "#/definitions/workspaceConnectionAccountKey", - "metadata": { - "description": "Required. The credentials for the connection." - } - } - } - }, - "apiKeyAuthWorkspaceConnectionPropertyType": { - "type": "object", - "properties": { - "authType": { - "type": "string", - "allowedValues": [ - "ApiKey" - ], - "metadata": { - "description": "Required. The authentication type of the connection target." - } - }, - "credentials": { - "$ref": "#/definitions/workspaceConnectionApiKeyType", - "metadata": { - "description": "Required. The credentials for the connection." - } - } - } - }, - "customKeysWorkspaceConnectionPropertyType": { - "type": "object", - "properties": { - "authType": { - "type": "string", - "allowedValues": [ - "CustomKeys" - ], - "metadata": { - "description": "Required. The authentication type of the connection target." - } - }, - "credentials": { - "$ref": "#/definitions/customKeysType", - "metadata": { - "description": "Required. The credentials for the connection." - } - } - } - }, - "managedIdentityAuthTypeWorkspaceConnectionPropertyType": { - "type": "object", - "properties": { - "authType": { - "type": "string", - "allowedValues": [ - "ManagedIdentity" - ], - "metadata": { - "description": "Required. The authentication type of the connection target." - } - }, - "credentials": { - "$ref": "#/definitions/workspaceConnectionManagedIdentityType", - "metadata": { - "description": "Required. The credentials for the connection." - } - } - } - }, - "noneAuthTypeWorkspaceConnectionPropertyType": { - "type": "object", - "properties": { - "authType": { - "type": "string", - "allowedValues": [ - "None" - ], - "metadata": { - "description": "Required. The authentication type of the connection target." - } - } - } - }, - "oauth2AuthTypeWorkspaceConnectionPropertyType": { - "type": "object", - "properties": { - "authType": { - "type": "string", - "allowedValues": [ - "OAuth2" - ], - "metadata": { - "description": "Required. The authentication type of the connection target." - } - }, - "credentials": { - "$ref": "#/definitions/workspaceConnectionOAuth2Type", - "metadata": { - "description": "Required. The credentials for the connection." - } - } - } - }, - "patAuthTypeWorkspaceConnectionPropertyType": { - "type": "object", - "properties": { - "authType": { - "type": "string", - "allowedValues": [ - "PAT" - ], - "metadata": { - "description": "Required. The authentication type of the connection target." - } - }, - "credentials": { - "$ref": "#/definitions/workspaceConnectionPersonalAccessTokenType", - "metadata": { - "description": "Required. The credentials for the connection." - } - } - } - }, - "sasAuthTypeWorkspaceConnectionPropertyType": { - "type": "object", - "properties": { - "authType": { - "type": "string", - "allowedValues": [ - "SAS" - ], - "metadata": { - "description": "Required. The authentication type of the connection target." - } - }, - "credentials": { - "$ref": "#/definitions/workspaceConnectionSharedAccessSignatureType", - "metadata": { - "description": "Required. The credentials for the connection." - } - } - } - }, - "servicePrincipalAuthTypeWorkspaceConnectionPropertyType": { - "type": "object", - "properties": { - "authType": { - "type": "string", - "allowedValues": [ - "ServicePrincipal" - ], - "metadata": { - "description": "Required. The authentication type of the connection target." - } - }, - "credentials": { - "$ref": "#/definitions/workspaceConnectionServicePrincipalType", - "metadata": { - "description": "Required. The credentials for the connection." - } - } - } - }, - "usernamePasswordAuthTypeWorkspaceConnectionPropertyType": { - "type": "object", - "properties": { - "authType": { - "type": "string", - "allowedValues": [ - "UsernamePassword" - ], - "metadata": { - "description": "Required. The authentication type of the connection target." - } - }, - "credentials": { - "$ref": "#/definitions/workspaceConnectionUsernamePasswordType", - "metadata": { - "description": "Required. The credentials for the connection." - } - } - } - }, - "customKeysType": { - "type": "object", - "properties": { - "keys": { - "type": "object", - "properties": {}, - "additionalProperties": { - "type": "string", - "metadata": { - "description": "Required. Key-value pairs for the custom keys." - } - }, - "metadata": { - "description": "Required. The custom keys for the connection." - } - } - } - }, - "workspaceConnectionAccessKeyType": { - "type": "object", - "properties": { - "accessKeyId": { - "type": "string", - "metadata": { - "description": "Required. The connection access key ID." - } - }, - "secretAccessKey": { - "type": "string", - "metadata": { - "description": "Required. The connection secret access key." - } - } - } - }, - "workspaceConnectionAccountKey": { - "type": "object", - "properties": { - "key": { - "type": "string", - "metadata": { - "description": "Required. The connection key." - } - } - } - }, - "workspaceConnectionApiKeyType": { - "type": "object", - "properties": { - "key": { - "type": "string", - "metadata": { - "description": "Required. The connection API key." - } - } - } - }, - "workspaceConnectionManagedIdentityType": { - "type": "object", - "properties": { - "clientId": { - "type": "string", - "metadata": { - "description": "Required. The connection managed identity ID." - } - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "Required. The connection managed identity resource ID." - } - } - } - }, - "workspaceConnectionOAuth2Type": { - "type": "object", - "properties": { - "authUrl": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Conditional. The connection auth URL. Required by Concur connection category." - } - }, - "clientId": { - "type": "string", - "minLength": 36, - "maxLength": 36, - "metadata": { - "description": "Required. The connection client ID in the format of UUID." - } - }, - "clientSecret": { - "type": "string", - "metadata": { - "description": "Required. The connection client secret." - } - }, - "developerToken": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Conditional. The connection developer token. Required by GoogleAdWords connection category." - } - }, - "password": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Conditional. The connection password. Required by Concur and ServiceNow connection categories where AccessToken grant type is 'Password'." - } - }, - "refreshToken": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Conditional. The connection refresh token. Required by GoogleBigQuery, GoogleAdWords, Hubspot, QuickBooks, Square, Xero and Zoho connection categories." - } - }, - "tenantId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Required. The connection tenant ID. Required by QuickBooks and Xero connection categories." - } - }, - "username": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Conditional. The connection username. Required by Concur and ServiceNow connection categories where AccessToken grant type is 'Password'." - } - } - } - }, - "workspaceConnectionPersonalAccessTokenType": { - "type": "object", - "properties": { - "pat": { - "type": "string", - "metadata": { - "description": "Required. The connection personal access token." - } - } - } - }, - "workspaceConnectionSharedAccessSignatureType": { - "type": "object", - "properties": { - "sas": { - "type": "string", - "metadata": { - "description": "Required. The connection SAS token." - } - } - } - }, - "workspaceConnectionServicePrincipalType": { - "type": "object", - "properties": { - "clientId": { - "type": "string", - "metadata": { - "description": "Required. The connection client ID." - } - }, - "clientSecret": { - "type": "string", - "metadata": { - "description": "Required. The connection client secret." - } - }, - "tenantId": { - "type": "string", - "metadata": { - "description": "Required. The connection tenant ID." - } - } - } - }, - "workspaceConnectionUsernamePasswordType": { - "type": "object", - "properties": { - "password": { - "type": "string", - "metadata": { - "description": "Required. The connection password." - } - }, - "securityToken": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Conditional. The connection security token. Required by connections like SalesForce for extra security in addition to 'UsernamePassword'." - } - }, - "username": { - "type": "string", - "metadata": { - "description": "Required. The connection username." - } - } - } - }, - "connectionPropertyType": { - "type": "secureObject", - "discriminator": { - "propertyName": "authType", - "mapping": { - "AAD": { - "$ref": "#/definitions/aadAuthTypeWorkspaceConnectionPropertyType" - }, - "AccessKey": { - "$ref": "#/definitions/accessKeyAuthTypeWorkspaceConnectionPropertyType" - }, - "ApiKey": { - "$ref": "#/definitions/apiKeyAuthWorkspaceConnectionPropertyType" - }, - "CustomKeys": { - "$ref": "#/definitions/customKeysWorkspaceConnectionPropertyType" - }, - "ManagedIdentity": { - "$ref": "#/definitions/managedIdentityAuthTypeWorkspaceConnectionPropertyType" - }, - "None": { - "$ref": "#/definitions/noneAuthTypeWorkspaceConnectionPropertyType" - }, - "OAuth2": { - "$ref": "#/definitions/oauth2AuthTypeWorkspaceConnectionPropertyType" - }, - "PAT": { - "$ref": "#/definitions/patAuthTypeWorkspaceConnectionPropertyType" - }, - "SAS": { - "$ref": "#/definitions/sasAuthTypeWorkspaceConnectionPropertyType" - }, - "ServicePrincipal": { - "$ref": "#/definitions/servicePrincipalAuthTypeWorkspaceConnectionPropertyType" - }, - "UsernamePassword": { - "$ref": "#/definitions/usernamePasswordAuthTypeWorkspaceConnectionPropertyType" - } - } - }, - "metadata": { - "__bicep_export!": true - } - } - }, - "parameters": { - "name": { - "type": "string", - "metadata": { - "description": "Required. Name of the connection to create." - } - }, - "machineLearningWorkspaceName": { - "type": "string", - "metadata": { - "description": "Required. The name of the parent Machine Learning Workspace. Required if the template is used in a standalone deployment." - } - }, - "category": { - "$ref": "#/definitions/categoryType", - "metadata": { - "description": "Required. Category of the connection." - } - }, - "expiryTime": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The expiry time of the connection." - } - }, - "isSharedToAll": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Indicates whether the connection is shared to all users in the workspace." - } - }, - "metadata": { - "$ref": "#/definitions/metadataType", - "defaultValue": {}, - "metadata": { - "description": "Optional. User metadata for the connection." - } - }, - "sharedUserList": { - "type": "array", - "items": { - "type": "string" - }, - "nullable": true, - "metadata": { - "description": "Optional. The shared user list of the connection." - } - }, - "target": { - "type": "string", - "metadata": { - "description": "Required. The target of the connection." - } - }, - "value": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Value details of the workspace connection." - } - }, - "connectionProperties": { - "$ref": "#/definitions/connectionPropertyType", - "metadata": { - "description": "Required. The properties of the connection, specific to the auth type." - } - } - }, - "resources": { - "machineLearningWorkspace": { - "existing": true, - "type": "Microsoft.MachineLearningServices/workspaces", - "apiVersion": "2022-10-01", - "name": "[parameters('machineLearningWorkspaceName')]" - }, - "connection": { - "type": "Microsoft.MachineLearningServices/workspaces/connections", - "apiVersion": "2024-04-01", - "name": "[format('{0}/{1}', parameters('machineLearningWorkspaceName'), parameters('name'))]", - "properties": "[union(createObject('category', parameters('category'), 'expiryTime', parameters('expiryTime'), 'isSharedToAll', parameters('isSharedToAll'), 'metadata', parameters('metadata'), 'sharedUserList', parameters('sharedUserList'), 'target', parameters('target'), 'value', parameters('value')), parameters('connectionProperties'))]" - } - }, - "outputs": { - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the connection." - }, - "value": "[resourceId('Microsoft.MachineLearningServices/workspaces/connections', parameters('machineLearningWorkspaceName'), parameters('name'))]" - }, - "name": { - "type": "string", - "metadata": { - "description": "The name of the connection." - }, - "value": "[parameters('name')]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The name of the resource group the connection was created in." - }, - "value": "[resourceGroup().name]" - } - } - } - }, - "dependsOn": [ - "workspace" - ] - }, - "workspace_privateEndpoints": { - "copy": { - "name": "workspace_privateEndpoints", - "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-workspace-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "resourceGroup": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupName'), '')]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'name'), format('pep-{0}-{1}-{2}', last(split(resourceId('Microsoft.MachineLearningServices/workspaces', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'amlworkspace'), copyIndex()))]" - }, - "privateLinkServiceConnections": "[if(not(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true())), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.MachineLearningServices/workspaces', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'amlworkspace'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.MachineLearningServices/workspaces', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'amlworkspace')))))), createObject('value', null()))]", - "manualPrivateLinkServiceConnections": "[if(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true()), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.MachineLearningServices/workspaces', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'amlworkspace'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.MachineLearningServices/workspaces', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'amlworkspace')), 'requestMessage', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualConnectionRequestMessage'), 'Manual approval required.'))))), createObject('value', null()))]", - "subnetResourceId": { - "value": "[coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId]" - }, - "enableTelemetry": { - "value": "[variables('enableReferencedModulesTelemetry')]" - }, - "location": { - "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'location'), reference(split(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location)]" - }, - "lock": { - "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" - }, - "privateDnsZoneGroup": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroup')]" - }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'roleAssignments')]" - }, - "tags": { - "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" - }, - "customDnsConfigs": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customDnsConfigs')]" - }, - "ipConfigurations": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'ipConfigurations')]" - }, - "applicationSecurityGroupResourceIds": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'applicationSecurityGroupResourceIds')]" - }, - "customNetworkInterfaceName": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customNetworkInterfaceName')]" - } - }, - "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.29.47.4906", - "templateHash": "3308873178893851812" - }, - "name": "Private Endpoints", - "description": "This module deploys a Private Endpoint.", - "owner": "Azure/module-maintainers" - }, - "definitions": { - "privateDnsZoneGroupType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the Private DNS Zone Group." - } - }, - "privateDnsZoneGroupConfigs": { - "type": "array", - "items": { - "$ref": "#/definitions/privateDnsZoneGroupConfigType" - }, - "metadata": { - "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." - } - } - } - }, - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } - } - } - }, - "nullable": true - }, - "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." - } - } - }, - "nullable": true - }, - "ipConfigurationsType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the resource that is unique within a resource group." - } - }, - "properties": { - "type": "object", - "properties": { - "groupId": { - "type": "string", - "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." - } - }, - "memberName": { - "type": "string", - "metadata": { - "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." - } - }, - "privateIPAddress": { - "type": "string", - "metadata": { - "description": "Required. A private IP address obtained from the private endpoint's subnet." - } - } - }, - "metadata": { - "description": "Required. Properties of private endpoint IP configurations." - } - } - } - }, - "nullable": true - }, - "manualPrivateLinkServiceConnectionsType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the private link service connection." - } - }, - "properties": { - "type": "object", - "properties": { - "groupIds": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." - } - }, - "privateLinkServiceId": { - "type": "string", - "metadata": { - "description": "Required. The resource id of private link service." - } - }, - "requestMessage": { - "type": "string", - "metadata": { - "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." - } - } - }, - "metadata": { - "description": "Required. Properties of private link service connection." - } - } - } - }, - "nullable": true - }, - "privateLinkServiceConnectionsType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the private link service connection." - } - }, - "properties": { - "type": "object", - "properties": { - "groupIds": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." - } - }, - "privateLinkServiceId": { - "type": "string", - "metadata": { - "description": "Required. The resource id of private link service." - } - }, - "requestMessage": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." - } - } - }, - "metadata": { - "description": "Required. Properties of private link service connection." - } - } - } - }, - "nullable": true - }, - "customDnsConfigType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "fqdn": { - "type": "string", - "metadata": { - "description": "Required. Fqdn that resolves to private endpoint IP address." - } - }, - "ipAddresses": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. A list of private IP addresses of the private endpoint." - } - } - } - }, - "nullable": true - }, - "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_imported_from!": { - "sourceTemplate": "private-dns-zone-group/main.bicep" - } - } - } - }, - "parameters": { - "name": { - "type": "string", - "metadata": { - "description": "Required. Name of the private endpoint resource to create." - } - }, - "subnetResourceId": { - "type": "string", - "metadata": { - "description": "Required. Resource ID of the subnet where the endpoint needs to be created." - } - }, - "applicationSecurityGroupResourceIds": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. Application security groups in which the private endpoint IP configuration is included." - } - }, - "customNetworkInterfaceName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The custom name of the network interface attached to the private endpoint." - } - }, - "ipConfigurations": { - "$ref": "#/definitions/ipConfigurationsType", - "metadata": { - "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." - } - }, - "privateDnsZoneGroup": { - "$ref": "#/definitions/privateDnsZoneGroupType", - "nullable": true, - "metadata": { - "description": "Optional. The private DNS zone group to configure for the private endpoint." - } - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]", - "metadata": { - "description": "Optional. Location for all Resources." - } - }, - "lock": { - "$ref": "#/definitions/lockType", - "metadata": { - "description": "Optional. The lock settings of the service." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "tags": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." - } - }, - "customDnsConfigs": { - "$ref": "#/definitions/customDnsConfigType", - "metadata": { - "description": "Optional. Custom DNS configurations." - } - }, - "manualPrivateLinkServiceConnections": { - "$ref": "#/definitions/manualPrivateLinkServiceConnectionsType", - "metadata": { - "description": "Optional. 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." - } - }, - "privateLinkServiceConnections": { - "$ref": "#/definitions/privateLinkServiceConnectionsType", - "metadata": { - "description": "Optional. A grouping of information about the connection to the remote resource." - } - }, - "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 (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" - } - }, - "resources": { - "avmTelemetry": { - "condition": "[parameters('enableTelemetry')]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2024-03-01", - "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.7.0', '.', '-'), 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": "2023-11-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": "[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": "2022-09-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.29.47.4906", - "templateHash": "5805178546717255803" - }, - "name": "Private Endpoint Private DNS Zone Groups", - "description": "This module deploys a Private Endpoint Private DNS Zone Group.", - "owner": "Azure/module-maintainers" - }, - "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 - } - } - }, - "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." - } - } - }, - "variables": { - "copy": [ - { - "name": "privateDnsZoneConfigsVar", - "count": "[length(parameters('privateDnsZoneConfigs'))]", - "input": { - "name": "[coalesce(tryGet(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')], 'name'), last(split(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId, '/')))]", - "properties": { - "privateDnsZoneId": "[parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId]" - } - } - } - ] - }, - "resources": { - "privateEndpoint": { - "existing": true, - "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2023-11-01", - "name": "[parameters('privateEndpointName')]" - }, - "privateDnsZoneGroup": { - "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2023-11-01", - "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", - "properties": { - "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" - }, - "dependsOn": [ - "privateEndpoint" - ] - } - }, - "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', '2023-11-01', 'full').location]" - }, - "customDnsConfig": { - "$ref": "#/definitions/customDnsConfigType", - "metadata": { - "description": "The custom DNS configurations of the private endpoint." - }, - "value": "[reference('privateEndpoint').customDnsConfigs]" - }, - "groupId": { - "type": "string", - "metadata": { - "description": "The group Id for the private endpoint Group." - }, - "value": "[if(and(not(empty(reference('privateEndpoint').manualPrivateLinkServiceConnections)), greater(length(tryGet(reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties, 'groupIds')), 0)), coalesce(tryGet(reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties, 'groupIds', 0), ''), if(and(not(empty(reference('privateEndpoint').privateLinkServiceConnections)), greater(length(tryGet(reference('privateEndpoint').privateLinkServiceConnections[0].properties, 'groupIds')), 0)), coalesce(tryGet(reference('privateEndpoint').privateLinkServiceConnections[0].properties, 'groupIds', 0), ''), ''))]" - } - } - } - }, - "dependsOn": [ - "workspace" - ] - } - }, - "outputs": { - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the machine learning service." - }, - "value": "[resourceId('Microsoft.MachineLearningServices/workspaces', parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group the machine learning service was deployed into." - }, - "value": "[resourceGroup().name]" - }, - "name": { - "type": "string", - "metadata": { - "description": "The name of the machine learning service." - }, - "value": "[parameters('name')]" - }, - "systemAssignedMIPrincipalId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "The principal ID of the system assigned identity." - }, - "value": "[tryGet(tryGet(reference('workspace', '2024-04-01-preview', 'full'), 'identity'), 'principalId')]" - }, - "location": { - "type": "string", - "metadata": { - "description": "The location the resource was deployed into." - }, - "value": "[reference('workspace', '2024-04-01-preview', 'full').location]" - } - } - } - }, - "dependsOn": [ - "mlApiPrivateDnsZone", - "mlNotebooksPrivateDnsZone" - ] - } - }, - "outputs": { - "resourceId": { - "type": "string", - "value": "[reference('hub').outputs.resourceId.value]" - }, - "name": { - "type": "string", - "value": "[reference('hub').outputs.name.value]" - } - } - } - }, - "dependsOn": [ - "aiSearch", - "applicationInsights", - "cognitiveServices", - "containerRegistry", - "keyvault", - "logAnalyticsWorkspace", - "network", - "storageAccount" - ] - }, - "aiProject": { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[take(format('{0}-ai-project-deployment', parameters('name')), 64)]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[format('proj-{0}', parameters('name'))]" - }, - "location": { - "value": "[parameters('location')]" - }, - "hubResourceId": { - "value": "[reference('aiHub').outputs.resourceId.value]" - }, - "networkIsolation": { - "value": "[parameters('networkIsolation')]" - }, - "logAnalyticsWorkspaceResourceId": { - "value": "[reference('logAnalyticsWorkspace').outputs.resourceId.value]" - }, - "roleAssignments": { - "value": "[union(if(empty(parameters('userObjectId')), createArray(), createArray(createObject('roleDefinitionIdOrName', 'f6c7c914-8db3-469d-8ca1-694a8f32e121', 'principalId', parameters('userObjectId'), 'principalType', 'User'))), createArray(createObject('roleDefinitionIdOrName', 'f6c7c914-8db3-469d-8ca1-694a8f32e121', 'principalId', reference('cognitiveServices').outputs.aiServicesSystemAssignedMIPrincipalId.value, 'principalType', 'ServicePrincipal')))]" - }, - "tags": { - "value": "[variables('allTags')]" - } - }, - "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.1.42791", - "templateHash": "1542994590355543426" - } - }, - "definitions": { - "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.5.1" - } - } - } - }, - "parameters": { - "name": { - "type": "string", - "metadata": { - "description": "Name of the AI Foundry Project." - } - }, - "location": { - "type": "string", - "metadata": { - "description": "Specifies the location for all the Azure resources." - } - }, - "tags": { - "type": "object", - "defaultValue": {}, - "metadata": { - "description": "Optional. Tags to be applied to the resources." - } - }, - "hubResourceId": { - "type": "string", - "metadata": { - "description": "Resource ID of the parent AI Hub." - } - }, - "logAnalyticsWorkspaceResourceId": { - "type": "string", - "metadata": { - "description": "Resource ID of the Log Analytics workspace to use for diagnostic settings." - } - }, - "networkIsolation": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Specifies whether network isolation is enabled to determine public access to the AI Project." - } - }, - "roleAssignments": { - "type": "array", - "items": { - "$ref": "#/definitions/roleAssignmentType" - }, - "nullable": true, - "metadata": { - "description": "Optional. Array of role assignments to create." - } - } - }, - "variables": { - "nameFormatted": "[toLower(parameters('name'))]" - }, - "resources": { - "aiProject": { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[take(format('{0}-ai-project-deployment', variables('nameFormatted')), 64)]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[variables('nameFormatted')]" - }, - "sku": { - "value": "Standard" - }, - "kind": { - "value": "Project" - }, - "location": { - "value": "[parameters('location')]" - }, - "hubResourceId": { - "value": "[parameters('hubResourceId')]" - }, - "managedIdentities": { - "value": { - "systemAssigned": true - } - }, - "publicNetworkAccess": "[if(parameters('networkIsolation'), createObject('value', 'Disabled'), createObject('value', 'Enabled'))]", - "hbiWorkspace": { - "value": false - }, - "systemDatastoresAuthMode": { - "value": "identity" - }, - "roleAssignments": { - "value": "[parameters('roleAssignments')]" - }, - "diagnosticSettings": { - "value": [ - { - "copy": [ - { - "name": "logCategoriesAndGroups", - "count": "[length(createArray('AmlComputeClusterEvent', 'AmlComputeClusterNodeEvent', 'AmlComputeJobEvent', 'AmlComputeCpuGpuUtilization', 'AmlRunStatusChangedEvent', 'ModelsChangeEvent', 'ModelsReadEvent', 'ModelsActionEvent', 'DeploymentReadEvent', 'DeploymentEventACI', 'DeploymentEventAKS', 'InferencingOperationAKS', 'InferencingOperationACI', 'EnvironmentChangeEvent', 'EnvironmentReadEvent', 'DataLabelChangeEvent', 'DataLabelReadEvent', 'DataSetChangeEvent', 'DataSetReadEvent', 'PipelineChangeEvent', 'PipelineReadEvent', 'RunEvent', 'RunReadEvent'))]", - "input": { - "category": "[createArray('AmlComputeClusterEvent', 'AmlComputeClusterNodeEvent', 'AmlComputeJobEvent', 'AmlComputeCpuGpuUtilization', 'AmlRunStatusChangedEvent', 'ModelsChangeEvent', 'ModelsReadEvent', 'ModelsActionEvent', 'DeploymentReadEvent', 'DeploymentEventACI', 'DeploymentEventAKS', 'InferencingOperationAKS', 'InferencingOperationACI', 'EnvironmentChangeEvent', 'EnvironmentReadEvent', 'DataLabelChangeEvent', 'DataLabelReadEvent', 'DataSetChangeEvent', 'DataSetReadEvent', 'PipelineChangeEvent', 'PipelineReadEvent', 'RunEvent', 'RunReadEvent')[copyIndex('logCategoriesAndGroups')]]" - } - } - ], - "workspaceResourceId": "[parameters('logAnalyticsWorkspaceResourceId')]", - "metricCategories": [ - { - "category": "AllMetrics" - } - ] - } - ] - }, - "tags": { - "value": "[parameters('tags')]" - } - }, - "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.33.93.31351", - "templateHash": "760529655692286424" - }, - "name": "Machine Learning Services Workspaces", - "description": "This module deploys a Machine Learning Services Workspace." - }, - "definitions": { - "featureStoreSettingType": { - "type": "object", - "properties": { - "computeRuntime": { - "type": "object", - "properties": { - "sparkRuntimeVersion": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The spark runtime version." - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. Compute runtime config for feature store type workspace." - } - }, - "offlineStoreConnectionName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The offline store connection name." - } - }, - "onlineStoreConnectionName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The online store connection name." - } - } - }, - "metadata": { - "__bicep_export!": true - } - }, - "outboundRuleType": { - "type": "object", - "discriminator": { - "propertyName": "type", - "mapping": { - "FQDN": { - "$ref": "#/definitions/fqdnoutboundRuleType" - }, - "PrivateEndpoint": { - "$ref": "#/definitions/privateEndpointoutboundRuleType" - }, - "ServiceTag": { - "$ref": "#/definitions/serviceTagoutboundRuleType" - } - } - }, - "metadata": { - "__bicep_export!": true - } - }, - "fqdnoutboundRuleType": { - "type": "object", - "properties": { - "type": { - "type": "string", - "allowedValues": [ - "FQDN" - ], - "metadata": { - "description": "Required. Type of a managed network Outbound Rule of a machine learning workspace. Only supported when 'isolationMode' is 'AllowOnlyApprovedOutbound'." - } - }, - "destination": { - "type": "string", - "metadata": { - "description": "Required. Fully Qualified Domain Name to allow for outbound traffic." - } - }, - "category": { - "type": "string", - "allowedValues": [ - "Dependency", - "Recommended", - "Required", - "UserDefined" - ], - "nullable": true, - "metadata": { - "description": "Optional. Category of a managed network Outbound Rule of a machine learning workspace." - } - } - }, - "metadata": { - "__bicep_export!": true - } - }, - "privateEndpointoutboundRuleType": { - "type": "object", - "properties": { - "type": { - "type": "string", - "allowedValues": [ - "PrivateEndpoint" - ], - "metadata": { - "description": "Required. Type of a managed network Outbound Rule of a machine learning workspace. Only supported when 'isolationMode' is 'AllowOnlyApprovedOutbound' or 'AllowInternetOutbound'." - } - }, - "destination": { - "type": "object", - "properties": { - "serviceResourceId": { - "type": "string", - "metadata": { - "description": "Required. The resource ID of the target resource for the private endpoint." - } - }, - "sparkEnabled": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Whether the private endpoint can be used by jobs running on Spark." - } - }, - "subresourceTarget": { - "type": "string", - "metadata": { - "description": "Required. The sub resource to connect for the private endpoint." - } - } - }, - "metadata": { - "description": "Required. Service Tag destination for a Service Tag Outbound Rule for the managed network of a machine learning workspace." - } - }, - "category": { - "type": "string", - "allowedValues": [ - "Dependency", - "Recommended", - "Required", - "UserDefined" - ], - "nullable": true, - "metadata": { - "description": "Optional. Category of a managed network Outbound Rule of a machine learning workspace." - } - } - }, - "metadata": { - "__bicep_export!": true - } - }, - "serviceTagoutboundRuleType": { - "type": "object", - "properties": { - "type": { - "type": "string", - "allowedValues": [ - "ServiceTag" - ], - "metadata": { - "description": "Required. Type of a managed network Outbound Rule of a machine learning workspace. Only supported when 'isolationMode' is 'AllowOnlyApprovedOutbound'." - } - }, - "destination": { - "type": "object", - "properties": { - "portRanges": { - "type": "string", - "metadata": { - "description": "Required. The name of the service tag to allow." - } - }, - "protocol": { - "type": "string", - "allowedValues": [ - "*", - "ICMP", - "TCP", - "UDP" - ], - "metadata": { - "description": "Required. The protocol to allow. Provide an asterisk(*) to allow any protocol." - } - }, - "serviceTag": { - "type": "string", - "metadata": { - "description": "Required. Which ports will be allow traffic by this rule. Provide an asterisk(*) to allow any port." - } - } - }, - "metadata": { - "description": "Required. Service Tag destination for a Service Tag Outbound Rule for the managed network of a machine learning workspace." - } - }, - "category": { - "type": "string", - "allowedValues": [ - "Dependency", - "Recommended", - "Required", - "UserDefined" - ], - "nullable": true, - "metadata": { - "description": "Optional. Category of a managed network Outbound Rule of a machine learning workspace." - } - } - }, - "metadata": { - "__bicep_export!": true - } - }, - "managedNetworkSettingType": { - "type": "object", - "properties": { - "isolationMode": { - "type": "string", - "allowedValues": [ - "AllowInternetOutbound", - "AllowOnlyApprovedOutbound", - "Disabled" - ], - "metadata": { - "description": "Required. Isolation mode for the managed network of a machine learning workspace." - } - }, - "outboundRules": { - "type": "object", - "properties": {}, - "additionalProperties": { - "$ref": "#/definitions/outboundRuleType", - "metadata": { - "description": "Required. The outbound rule. The name of the rule is the object key." - } - }, - "nullable": true, - "metadata": { - "description": "Optional. Outbound rules for the managed network of a machine learning workspace." - } - } - }, - "metadata": { - "__bicep_export!": true - } - }, - "serverlessComputeSettingType": { - "type": "object", - "properties": { - "serverlessComputeCustomSubnet": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The resource ID of an existing virtual network subnet in which serverless compute nodes should be deployed." - } - }, - "serverlessComputeNoPublicIP": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. The flag to signal if serverless compute nodes deployed in custom vNet would have no public IP addresses for a workspace with private endpoint." - } - } - }, - "metadata": { - "__bicep_export!": true - } - }, - "workspaceHubConfigType": { - "type": "object", - "properties": { - "additionalWorkspaceStorageAccounts": { - "type": "array", - "items": { - "type": "string" - }, - "nullable": true, - "metadata": { - "description": "Optional. The resource IDs of additional storage accounts to attach to the workspace." - } - }, - "defaultWorkspaceResourceGroup": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The resource ID of the default resource group for projects created in the workspace hub." - } - } - }, - "metadata": { - "__bicep_export!": true - } - }, - "connectionType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. Name of the connection to create." - } - }, - "category": { - "$ref": "#/definitions/categoryType", - "metadata": { - "description": "Required. Category of the connection." - } - }, - "expiryTime": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The expiry time of the connection." - } - }, - "isSharedToAll": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Indicates whether the connection is shared to all users in the workspace." - } - }, - "metadata": { - "type": "object", - "properties": {}, - "additionalProperties": { - "type": "string", - "metadata": { - "description": "Required. The metadata key-value pairs." - } - }, - "nullable": true, - "metadata": { - "description": "Optional. User metadata for the connection." - } - }, - "sharedUserList": { - "type": "array", - "items": { - "type": "string" - }, - "nullable": true, - "metadata": { - "description": "Optional. The shared user list of the connection." - } - }, - "target": { - "type": "string", - "metadata": { - "description": "Required. The target of the connection." - } - }, - "value": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Value details of the workspace connection." - } - }, - "connectionProperties": { - "$ref": "#/definitions/connectionPropertyType", - "metadata": { - "description": "Required. The properties of the connection, specific to the auth type." - } - } - }, - "metadata": { - "__bicep_export!": true - } - }, - "_1.privateEndpointCustomDnsConfigType": { - "type": "object", - "properties": { - "fqdn": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. FQDN that resolves to private endpoint IP address." - } - }, - "ipAddresses": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. A list of private IP addresses of the private endpoint." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" - } - } - }, - "_1.privateEndpointIpConfigurationType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the resource that is unique within a resource group." - } - }, - "properties": { - "type": "object", - "properties": { - "groupId": { - "type": "string", - "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." - } - }, - "memberName": { - "type": "string", - "metadata": { - "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to." - } - }, - "privateIPAddress": { - "type": "string", - "metadata": { - "description": "Required. A private IP address obtained from the private endpoint's subnet." - } - } - }, - "metadata": { - "description": "Required. Properties of private endpoint IP configurations." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" - } - } - }, - "_1.privateEndpointPrivateDnsZoneGroupType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the Private DNS Zone Group." - } - }, - "privateDnsZoneGroupConfigs": { - "type": "array", - "items": { - "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": "Required. The private DNS Zone Groups to associate the Private Endpoint. A DNS Zone Group can support up to 5 DNS zones." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" - } - } - }, - "_2.aadAuthTypeWorkspaceConnectionPropertyType": { - "type": "object", - "properties": { - "authType": { - "type": "string", - "allowedValues": [ - "AAD" - ], - "metadata": { - "description": "Required. The authentication type of the connection target." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "connection/main.bicep" - } - } - }, - "_2.accessKeyAuthTypeWorkspaceConnectionPropertyType": { - "type": "object", - "properties": { - "authType": { - "type": "string", - "allowedValues": [ - "AccessKey" - ], - "metadata": { - "description": "Required. The authentication type of the connection target." - } - }, - "credentials": { - "$ref": "#/definitions/_2.workspaceConnectionAccessKeyType", - "metadata": { - "description": "Required. The credentials for the connection." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "connection/main.bicep" - } - } - }, - "_2.apiKeyAuthWorkspaceConnectionPropertyType": { - "type": "object", - "properties": { - "authType": { - "type": "string", - "allowedValues": [ - "ApiKey" - ], - "metadata": { - "description": "Required. The authentication type of the connection target." - } - }, - "credentials": { - "$ref": "#/definitions/_2.workspaceConnectionApiKeyType", - "metadata": { - "description": "Required. The credentials for the connection." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "connection/main.bicep" - } - } - }, - "_2.customKeysType": { - "type": "object", - "properties": { - "keys": { - "type": "object", - "properties": {}, - "additionalProperties": { - "type": "string", - "metadata": { - "description": "Required. Key-value pairs for the custom keys." - } - }, - "metadata": { - "description": "Required. The custom keys for the connection." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "connection/main.bicep" - } - } - }, - "_2.customKeysWorkspaceConnectionPropertyType": { - "type": "object", - "properties": { - "authType": { - "type": "string", - "allowedValues": [ - "CustomKeys" - ], - "metadata": { - "description": "Required. The authentication type of the connection target." - } - }, - "credentials": { - "$ref": "#/definitions/_2.customKeysType", - "metadata": { - "description": "Required. The credentials for the connection." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "connection/main.bicep" - } - } - }, - "_2.managedIdentityAuthTypeWorkspaceConnectionPropertyType": { - "type": "object", - "properties": { - "authType": { - "type": "string", - "allowedValues": [ - "ManagedIdentity" - ], - "metadata": { - "description": "Required. The authentication type of the connection target." - } - }, - "credentials": { - "$ref": "#/definitions/_2.workspaceConnectionManagedIdentityType", - "metadata": { - "description": "Required. The credentials for the connection." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "connection/main.bicep" - } - } - }, - "_2.noneAuthTypeWorkspaceConnectionPropertyType": { - "type": "object", - "properties": { - "authType": { - "type": "string", - "allowedValues": [ - "None" - ], - "metadata": { - "description": "Required. The authentication type of the connection target." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "connection/main.bicep" - } - } - }, - "_2.oauth2AuthTypeWorkspaceConnectionPropertyType": { - "type": "object", - "properties": { - "authType": { - "type": "string", - "allowedValues": [ - "OAuth2" - ], - "metadata": { - "description": "Required. The authentication type of the connection target." - } - }, - "credentials": { - "$ref": "#/definitions/_2.workspaceConnectionOAuth2Type", - "metadata": { - "description": "Required. The credentials for the connection." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "connection/main.bicep" - } - } - }, - "_2.patAuthTypeWorkspaceConnectionPropertyType": { - "type": "object", - "properties": { - "authType": { - "type": "string", - "allowedValues": [ - "PAT" - ], - "metadata": { - "description": "Required. The authentication type of the connection target." - } - }, - "credentials": { - "$ref": "#/definitions/_2.workspaceConnectionPersonalAccessTokenType", - "metadata": { - "description": "Required. The credentials for the connection." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "connection/main.bicep" - } - } - }, - "_2.sasAuthTypeWorkspaceConnectionPropertyType": { - "type": "object", - "properties": { - "authType": { - "type": "string", - "allowedValues": [ - "SAS" - ], - "metadata": { - "description": "Required. The authentication type of the connection target." - } - }, - "credentials": { - "$ref": "#/definitions/_2.workspaceConnectionSharedAccessSignatureType", - "metadata": { - "description": "Required. The credentials for the connection." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "connection/main.bicep" - } - } - }, - "_2.servicePrincipalAuthTypeWorkspaceConnectionPropertyType": { - "type": "object", - "properties": { - "authType": { - "type": "string", - "allowedValues": [ - "ServicePrincipal" - ], - "metadata": { - "description": "Required. The authentication type of the connection target." - } - }, - "credentials": { - "$ref": "#/definitions/_2.workspaceConnectionServicePrincipalType", - "metadata": { - "description": "Required. The credentials for the connection." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "connection/main.bicep" - } - } - }, - "_2.usernamePasswordAuthTypeWorkspaceConnectionPropertyType": { - "type": "object", - "properties": { - "authType": { - "type": "string", - "allowedValues": [ - "UsernamePassword" - ], - "metadata": { - "description": "Required. The authentication type of the connection target." - } - }, - "credentials": { - "$ref": "#/definitions/_2.workspaceConnectionUsernamePasswordType", - "metadata": { - "description": "Required. The credentials for the connection." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "connection/main.bicep" - } - } - }, - "_2.workspaceConnectionAccessKeyType": { - "type": "object", - "properties": { - "accessKeyId": { - "type": "string", - "metadata": { - "description": "Required. The connection access key ID." - } - }, - "secretAccessKey": { - "type": "string", - "metadata": { - "description": "Required. The connection secret access key." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "connection/main.bicep" - } - } - }, - "_2.workspaceConnectionApiKeyType": { - "type": "object", - "properties": { - "key": { - "type": "string", - "metadata": { - "description": "Required. The connection API key." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "connection/main.bicep" - } - } - }, - "_2.workspaceConnectionManagedIdentityType": { - "type": "object", - "properties": { - "clientId": { - "type": "string", - "metadata": { - "description": "Required. The connection managed identity ID." - } - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "Required. The connection managed identity resource ID." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "connection/main.bicep" - } - } - }, - "_2.workspaceConnectionOAuth2Type": { - "type": "object", - "properties": { - "authUrl": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Conditional. The connection auth URL. Required by Concur connection category." - } - }, - "clientId": { - "type": "string", - "minLength": 36, - "maxLength": 36, - "metadata": { - "description": "Required. The connection client ID in the format of UUID." - } - }, - "clientSecret": { - "type": "string", - "metadata": { - "description": "Required. The connection client secret." - } - }, - "developerToken": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Conditional. The connection developer token. Required by GoogleAdWords connection category." - } - }, - "password": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Conditional. The connection password. Required by Concur and ServiceNow connection categories where AccessToken grant type is 'Password'." - } - }, - "refreshToken": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Conditional. The connection refresh token. Required by GoogleBigQuery, GoogleAdWords, Hubspot, QuickBooks, Square, Xero and Zoho connection categories." - } - }, - "tenantId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Required. The connection tenant ID. Required by QuickBooks and Xero connection categories." - } - }, - "username": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Conditional. The connection username. Required by Concur and ServiceNow connection categories where AccessToken grant type is 'Password'." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "connection/main.bicep" - } - } - }, - "_2.workspaceConnectionPersonalAccessTokenType": { - "type": "object", - "properties": { - "pat": { - "type": "string", - "metadata": { - "description": "Required. The connection personal access token." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "connection/main.bicep" - } - } - }, - "_2.workspaceConnectionServicePrincipalType": { - "type": "object", - "properties": { - "clientId": { - "type": "string", - "metadata": { - "description": "Required. The connection client ID." - } - }, - "clientSecret": { - "type": "string", - "metadata": { - "description": "Required. The connection client secret." - } - }, - "tenantId": { - "type": "string", - "metadata": { - "description": "Required. The connection tenant ID." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "connection/main.bicep" - } - } - }, - "_2.workspaceConnectionSharedAccessSignatureType": { - "type": "object", - "properties": { - "sas": { - "type": "string", - "metadata": { - "description": "Required. The connection SAS token." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "connection/main.bicep" - } - } - }, - "_2.workspaceConnectionUsernamePasswordType": { - "type": "object", - "properties": { - "password": { - "type": "string", - "metadata": { - "description": "Required. The connection password." - } - }, - "securityToken": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Conditional. The connection security token. Required by connections like SalesForce for extra security in addition to 'UsernamePassword'." - } - }, - "username": { - "type": "string", - "metadata": { - "description": "Required. The connection username." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "connection/main.bicep" - } - } - }, - "categoryType": { - "type": "string", - "allowedValues": [ - "ADLSGen2", - "AIServices", - "AmazonMws", - "AmazonRdsForOracle", - "AmazonRdsForSqlServer", - "AmazonRedshift", - "AmazonS3Compatible", - "ApiKey", - "AzureBlob", - "AzureDataExplorer", - "AzureDatabricksDeltaLake", - "AzureMariaDb", - "AzureMySqlDb", - "AzureOneLake", - "AzureOpenAI", - "AzurePostgresDb", - "AzureSqlDb", - "AzureSqlMi", - "AzureSynapseAnalytics", - "AzureTableStorage", - "BingLLMSearch", - "Cassandra", - "CognitiveSearch", - "CognitiveService", - "Concur", - "ContainerRegistry", - "CosmosDb", - "CosmosDbMongoDbApi", - "Couchbase", - "CustomKeys", - "Db2", - "Drill", - "Dynamics", - "DynamicsAx", - "DynamicsCrm", - "Eloqua", - "FileServer", - "FtpServer", - "GenericContainerRegistry", - "GenericHttp", - "GenericRest", - "Git", - "GoogleAdWords", - "GoogleBigQuery", - "GoogleCloudStorage", - "Greenplum", - "Hbase", - "Hdfs", - "Hive", - "Hubspot", - "Impala", - "Informix", - "Jira", - "Magento", - "MariaDb", - "Marketo", - "MicrosoftAccess", - "MongoDbAtlas", - "MongoDbV2", - "MySql", - "Netezza", - "ODataRest", - "Odbc", - "Office365", - "OpenAI", - "Oracle", - "OracleCloudStorage", - "OracleServiceCloud", - "PayPal", - "Phoenix", - "PostgreSql", - "Presto", - "PythonFeed", - "QuickBooks", - "Redis", - "Responsys", - "S3", - "Salesforce", - "SalesforceMarketingCloud", - "SalesforceServiceCloud", - "SapBw", - "SapCloudForCustomer", - "SapEcc", - "SapHana", - "SapOpenHub", - "SapTable", - "Serp", - "Serverless", - "ServiceNow", - "Sftp", - "SharePointOnlineList", - "Shopify", - "Snowflake", - "Spark", - "SqlServer", - "Square", - "Sybase", - "Teradata", - "Vertica", - "WebTable", - "Xero", - "Zoho" - ], - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "connection/main.bicep" - } - } - }, - "connectionPropertyType": { - "type": "secureObject", - "discriminator": { - "propertyName": "authType", - "mapping": { - "AAD": { - "$ref": "#/definitions/_2.aadAuthTypeWorkspaceConnectionPropertyType" - }, - "AccessKey": { - "$ref": "#/definitions/_2.accessKeyAuthTypeWorkspaceConnectionPropertyType" - }, - "ApiKey": { - "$ref": "#/definitions/_2.apiKeyAuthWorkspaceConnectionPropertyType" - }, - "CustomKeys": { - "$ref": "#/definitions/_2.customKeysWorkspaceConnectionPropertyType" - }, - "ManagedIdentity": { - "$ref": "#/definitions/_2.managedIdentityAuthTypeWorkspaceConnectionPropertyType" - }, - "None": { - "$ref": "#/definitions/_2.noneAuthTypeWorkspaceConnectionPropertyType" - }, - "OAuth2": { - "$ref": "#/definitions/_2.oauth2AuthTypeWorkspaceConnectionPropertyType" - }, - "PAT": { - "$ref": "#/definitions/_2.patAuthTypeWorkspaceConnectionPropertyType" - }, - "SAS": { - "$ref": "#/definitions/_2.sasAuthTypeWorkspaceConnectionPropertyType" - }, - "ServicePrincipal": { - "$ref": "#/definitions/_2.servicePrincipalAuthTypeWorkspaceConnectionPropertyType" - }, - "UsernamePassword": { - "$ref": "#/definitions/_2.usernamePasswordAuthTypeWorkspaceConnectionPropertyType" - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "connection/main.bicep" - } - } - }, - "customerManagedKeyType": { - "type": "object", - "properties": { - "keyVaultResourceId": { - "type": "string", - "metadata": { - "description": "Required. The resource ID of a key vault to reference a customer managed key for encryption from." - } - }, - "keyName": { - "type": "string", - "metadata": { - "description": "Required. The name of the customer managed key to use for encryption." - } - }, - "keyVersion": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The version of the customer managed key to reference for encryption. If not provided, the deployment will use the latest version available at deployment time." - } - }, - "userAssignedIdentityResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use." - } - } - }, - "metadata": { - "description": "An AVM-aligned type for a customer-managed key. To be used if the resource type does not support auto-rotation of the customer-managed key.", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" - } - } - }, - "diagnosticSettingFullType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the diagnostic setting." - } - }, - "logCategoriesAndGroups": { - "type": "array", - "items": { - "type": "object", - "properties": { - "category": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." - } - }, - "categoryGroup": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." - } - }, - "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 logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." - } - }, - "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 both logs & metrics are supported by the resource provider.", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.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." - } - } - }, - "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.4.0" - } - } - }, - "managedIdentityAllType": { - "type": "object", - "properties": { - "systemAssigned": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Enables system assigned managed identity on the resource." - } - }, - "userAssignedResourceIds": { - "type": "array", - "items": { - "type": "string" - }, - "nullable": true, - "metadata": { - "description": "Optional. The resource ID(s) to assign to the resource. Required if a user assigned identity is used for encryption." - } - } - }, - "metadata": { - "description": "An AVM-aligned type for a managed identity configuration. To be used if both a system-assigned & user-assigned identities are supported by the resource provider.", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" - } - } - }, - "privateEndpointSingleServiceType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the Private Endpoint." - } - }, - "location": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The location to deploy the Private Endpoint to." - } - }, - "privateLinkServiceConnectionName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the private link connection to create." - } - }, - "service": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The subresource to deploy the Private Endpoint for. For example \"vault\" for a Key Vault Private Endpoint." - } - }, - "subnetResourceId": { - "type": "string", - "metadata": { - "description": "Required. Resource ID of the subnet where the endpoint needs to be created." - } - }, - "privateDnsZoneGroup": { - "$ref": "#/definitions/_1.privateEndpointPrivateDnsZoneGroupType", - "nullable": true, - "metadata": { - "description": "Optional. The private DNS Zone Group to configure for the Private Endpoint." - } - }, - "isManualConnection": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. If Manual Private Link Connection is required." - } - }, - "manualConnectionRequestMessage": { - "type": "string", - "nullable": true, - "maxLength": 140, - "metadata": { - "description": "Optional. A message passed to the owner of the remote resource with the manual connection request." - } - }, - "customDnsConfigs": { - "type": "array", - "items": { - "$ref": "#/definitions/_1.privateEndpointCustomDnsConfigType" - }, - "nullable": true, - "metadata": { - "description": "Optional. Custom DNS configurations." - } - }, - "ipConfigurations": { - "type": "array", - "items": { - "$ref": "#/definitions/_1.privateEndpointIpConfigurationType" - }, - "nullable": true, - "metadata": { - "description": "Optional. A list of IP configurations of the Private Endpoint. This will be used to map to the first-party Service endpoints." - } - }, - "applicationSecurityGroupResourceIds": { - "type": "array", - "items": { - "type": "string" - }, - "nullable": true, - "metadata": { - "description": "Optional. Application security groups in which the Private Endpoint IP configuration is included." - } - }, - "customNetworkInterfaceName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The custom name of the network interface attached to the Private Endpoint." - } - }, - "lock": { - "$ref": "#/definitions/lockType", - "nullable": true, - "metadata": { - "description": "Optional. Specify the type of lock." - } - }, - "roleAssignments": { - "type": "array", - "items": { - "$ref": "#/definitions/roleAssignmentType" - }, - "nullable": true, - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "tags": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Tags to be applied on all resources/Resource Groups in this deployment." - } - }, - "enableTelemetry": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Enable/Disable usage telemetry for module." - } - }, - "resourceGroupName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Specify if you want to deploy the Private Endpoint into a different Resource Group than the main resource." - } - } - }, - "metadata": { - "description": "An AVM-aligned type for a private endpoint. To be used if the private endpoint's default service / groupId can be assumed (i.e., for services that only have one Private Endpoint type like 'vault' for key vault).", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.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.4.0" - } - } - } - }, - "parameters": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the machine learning workspace." - } - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]", - "metadata": { - "description": "Optional. Location for all resources." - } - }, - "sku": { - "type": "string", - "allowedValues": [ - "Free", - "Basic", - "Standard", - "Premium" - ], - "metadata": { - "description": "Required. Specifies the SKU, also referred as 'edition' of the Azure Machine Learning workspace." - } - }, - "kind": { - "type": "string", - "defaultValue": "Default", - "allowedValues": [ - "Default", - "Project", - "Hub", - "FeatureStore" - ], - "metadata": { - "description": "Optional. The type of Azure Machine Learning workspace to create." - } - }, - "associatedStorageAccountResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Conditional. The resource ID of the associated Storage Account. Required if 'kind' is 'Default', 'FeatureStore' or 'Hub'." - } - }, - "associatedKeyVaultResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Conditional. The resource ID of the associated Key Vault. Required if 'kind' is 'Default', 'FeatureStore' or 'Hub'." - } - }, - "associatedApplicationInsightsResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Conditional. The resource ID of the associated Application Insights. Required if 'kind' is 'Default' or 'FeatureStore'." - } - }, - "associatedContainerRegistryResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The resource ID of the associated Container Registry." - } - }, - "lock": { - "$ref": "#/definitions/lockType", - "nullable": true, - "metadata": { - "description": "Optional. The lock settings of the service." - } - }, - "hbiWorkspace": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "Optional. The flag to signal HBI data in the workspace and reduce diagnostic data collected by the service." - } - }, - "hubResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Conditional. The resource ID of the hub to associate with the workspace. Required if 'kind' is set to 'Project'." - } - }, - "roleAssignments": { - "type": "array", - "items": { - "$ref": "#/definitions/roleAssignmentType" - }, - "nullable": true, - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "privateEndpoints": { - "type": "array", - "items": { - "$ref": "#/definitions/privateEndpointSingleServiceType" - }, - "nullable": true, - "metadata": { - "description": "Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible." - } - }, - "computes": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. Computes to create respectively attach to the workspace." - } - }, - "connections": { - "type": "array", - "items": { - "$ref": "#/definitions/connectionType" - }, - "defaultValue": [], - "metadata": { - "description": "Optional. Connections to create in the workspace." - } - }, - "tags": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Resource tags." - } - }, - "enableTelemetry": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Optional. Enable/Disable usage telemetry for module." - } - }, - "managedIdentities": { - "$ref": "#/definitions/managedIdentityAllType", - "defaultValue": { - "systemAssigned": true - }, - "metadata": { - "description": "Optional. The managed identity definition for this resource. At least one identity type is required." - } - }, - "featureStoreSettings": { - "$ref": "#/definitions/featureStoreSettingType", - "nullable": true, - "metadata": { - "description": "Conditional. Settings for feature store type workspaces. Required if 'kind' is set to 'FeatureStore'." - } - }, - "managedNetworkSettings": { - "$ref": "#/definitions/managedNetworkSettingType", - "nullable": true, - "metadata": { - "description": "Optional. Managed Network settings for a machine learning workspace." - } - }, - "serverlessComputeSettings": { - "$ref": "#/definitions/serverlessComputeSettingType", - "nullable": true, - "metadata": { - "description": "Optional. Settings for serverless compute created in the workspace." - } - }, - "systemDatastoresAuthMode": { - "type": "string", - "nullable": true, - "allowedValues": [ - "accessKey", - "identity" - ], - "metadata": { - "description": "Optional. The authentication mode used by the workspace when connecting to the default storage account." - } - }, - "workspaceHubConfig": { - "$ref": "#/definitions/workspaceHubConfigType", - "nullable": true, - "metadata": { - "description": "Optional. Configuration for workspace hub settings." - } - }, - "diagnosticSettings": { - "type": "array", - "items": { - "$ref": "#/definitions/diagnosticSettingFullType" - }, - "nullable": true, - "metadata": { - "description": "Optional. The diagnostic settings of the service." - } - }, - "description": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The description of this workspace." - } - }, - "discoveryUrl": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. URL for the discovery service to identify regional endpoints for machine learning experimentation services." - } - }, - "customerManagedKey": { - "$ref": "#/definitions/customerManagedKeyType", - "nullable": true, - "metadata": { - "description": "Optional. The customer managed key definition." - } - }, - "imageBuildCompute": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The compute name for image build." - } - }, - "primaryUserAssignedIdentity": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Conditional. The user assigned identity resource ID that represents the workspace identity. Required if 'userAssignedIdentities' is not empty and may not be used if 'systemAssignedIdentity' is enabled." - } - }, - "serviceManagedResourcesSettings": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The service managed resource settings." - } - }, - "sharedPrivateLinkResources": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. The list of shared private link resources in this workspace. Note: This property is not idempotent." - } - }, - "publicNetworkAccess": { - "type": "string", - "defaultValue": "Disabled", - "allowedValues": [ - "Enabled", - "Disabled" - ], - "metadata": { - "description": "Optional. Whether or not public network access is allowed for this resource. For security reasons it should be disabled." - } - } - }, - "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)))))]" - } - ], - "enableReferencedModulesTelemetry": false, - "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", - "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned,UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', 'None')), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", - "builtInRoleNames": { - "AzureML Compute Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e503ece1-11d0-4e8e-8e2c-7a6c3bf38815')]", - "AzureML Data Scientist": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f6c7c914-8db3-469d-8ca1-694a8f32e121')]", - "AzureML Metrics Writer (preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '635dd51f-9968-44d3-b7fb-6d9a6bd613ae')]", - "AzureML Registry User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1823dd4f-9b8c-4ab6-ab4e-7397a3684615')]", - "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" - } - }, - "resources": { - "cMKKeyVault::cMKKey": { - "condition": "[and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), not(empty(tryGet(parameters('customerManagedKey'), 'keyName')))))]", - "existing": true, - "type": "Microsoft.KeyVault/vaults/keys", - "apiVersion": "2023-02-01", - "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '//'), '/')[2]]", - "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '////'), '/')[4]]", - "name": "[format('{0}/{1}', last(split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), 'dummyVault'), '/')), coalesce(tryGet(parameters('customerManagedKey'), 'keyName'), 'dummyKey'))]" - }, - "avmTelemetry": { - "condition": "[parameters('enableTelemetry')]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2024-03-01", - "name": "[format('46d3xbcp.res.machinelearningservices-workspace.{0}.{1}', replace('0.10.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" - } - } - } - } - }, - "cMKKeyVault": { - "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId')))]", - "existing": true, - "type": "Microsoft.KeyVault/vaults", - "apiVersion": "2023-02-01", - "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '//'), '/')[2]]", - "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '////'), '/')[4]]", - "name": "[last(split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), 'dummyVault'), '/'))]" - }, - "cMKUserAssignedIdentity": { - "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId')))]", - "existing": true, - "type": "Microsoft.ManagedIdentity/userAssignedIdentities", - "apiVersion": "2023-01-31", - "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '//'), '/')[2]]", - "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '////'), '/')[4]]", - "name": "[last(split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), 'dummyMsi'), '/'))]" - }, - "workspace": { - "type": "Microsoft.MachineLearningServices/workspaces", - "apiVersion": "2024-04-01-preview", - "name": "[parameters('name')]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", - "sku": { - "name": "[parameters('sku')]", - "tier": "[parameters('sku')]" - }, - "identity": "[variables('identity')]", - "properties": "[shallowMerge(createArray(createObject('friendlyName', parameters('name'), 'storageAccount', parameters('associatedStorageAccountResourceId'), 'keyVault', parameters('associatedKeyVaultResourceId'), 'applicationInsights', parameters('associatedApplicationInsightsResourceId'), 'containerRegistry', parameters('associatedContainerRegistryResourceId'), 'hbiWorkspace', parameters('hbiWorkspace'), 'description', parameters('description'), 'discoveryUrl', parameters('discoveryUrl'), 'encryption', if(not(empty(parameters('customerManagedKey'))), createObject('status', 'Enabled', 'identity', if(not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'))), createObject('userAssignedIdentity', extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '//'), '/')[2], split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '////'), '/')[4]), 'Microsoft.ManagedIdentity/userAssignedIdentities', last(split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), 'dummyMsi'), '/')))), null()), 'keyVaultProperties', createObject('keyVaultArmId', extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '//'), '/')[2], split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '////'), '/')[4]), 'Microsoft.KeyVault/vaults', last(split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), 'dummyVault'), '/'))), 'keyIdentifier', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'keyVersion'), ''))), format('{0}/{1}', reference('cMKKeyVault::cMKKey').keyUri, parameters('customerManagedKey').keyVersion), reference('cMKKeyVault::cMKKey').keyUriWithVersion))), null()), 'imageBuildCompute', parameters('imageBuildCompute'), 'primaryUserAssignedIdentity', parameters('primaryUserAssignedIdentity'), 'systemDatastoresAuthMode', parameters('systemDatastoresAuthMode'), 'publicNetworkAccess', parameters('publicNetworkAccess'), 'serviceManagedResourcesSettings', parameters('serviceManagedResourcesSettings'), 'featureStoreSettings', parameters('featureStoreSettings'), 'hubResourceId', parameters('hubResourceId'), 'managedNetwork', parameters('managedNetworkSettings'), 'serverlessComputeSettings', parameters('serverlessComputeSettings'), 'workspaceHubConfig', parameters('workspaceHubConfig')), if(not(empty(parameters('sharedPrivateLinkResources'))), createObject('sharedPrivateLinkResources', parameters('sharedPrivateLinkResources')), createObject())))]", - "kind": "[parameters('kind')]", - "dependsOn": [ - "cMKKeyVault::cMKKey" - ] - }, - "workspace_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.MachineLearningServices/workspaces/{0}', parameters('name'))]", - "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", - "properties": { - "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", - "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" - }, - "dependsOn": [ - "workspace" - ] - }, - "workspace_diagnosticSettings": { - "copy": { - "name": "workspace_diagnosticSettings", - "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" - }, - "type": "Microsoft.Insights/diagnosticSettings", - "apiVersion": "2021-05-01-preview", - "scope": "[format('Microsoft.MachineLearningServices/workspaces/{0}', parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", - "properties": { - "copy": [ - { - "name": "metrics", - "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", - "input": { - "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", - "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", - "timeGrain": null - } - }, - { - "name": "logs", - "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", - "input": { - "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", - "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", - "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" - } - } - ], - "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", - "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", - "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", - "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", - "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", - "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" - }, - "dependsOn": [ - "workspace" - ] - }, - "workspace_roleAssignments": { - "copy": { - "name": "workspace_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.MachineLearningServices/workspaces/{0}', parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.MachineLearningServices/workspaces', 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": [ - "workspace" - ] - }, - "workspace_computes": { - "copy": { - "name": "workspace_computes", - "count": "[length(coalesce(parameters('computes'), createArray()))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-{1}-compute', parameters('name'), coalesce(parameters('computes'), createArray())[copyIndex()].name)]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "machineLearningWorkspaceName": { - "value": "[parameters('name')]" - }, - "name": { - "value": "[coalesce(parameters('computes'), createArray())[copyIndex()].name]" - }, - "location": { - "value": "[coalesce(parameters('computes'), createArray())[copyIndex()].location]" - }, - "sku": { - "value": "[tryGet(coalesce(parameters('computes'), createArray())[copyIndex()], 'sku')]" - }, - "managedIdentities": { - "value": "[tryGet(coalesce(parameters('computes'), createArray())[copyIndex()], 'managedIdentities')]" - }, - "tags": { - "value": "[tryGet(coalesce(parameters('computes'), createArray())[copyIndex()], 'tags')]" - }, - "deployCompute": { - "value": "[tryGet(coalesce(parameters('computes'), createArray())[copyIndex()], 'deployCompute')]" - }, - "computeLocation": { - "value": "[tryGet(coalesce(parameters('computes'), createArray())[copyIndex()], 'computeLocation')]" - }, - "description": { - "value": "[tryGet(coalesce(parameters('computes'), createArray())[copyIndex()], 'description')]" - }, - "disableLocalAuth": { - "value": "[tryGet(coalesce(parameters('computes'), createArray())[copyIndex()], 'disableLocalAuth')]" - }, - "resourceId": { - "value": "[tryGet(coalesce(parameters('computes'), createArray())[copyIndex()], 'resourceId')]" - }, - "computeType": { - "value": "[coalesce(parameters('computes'), createArray())[copyIndex()].computeType]" - }, - "properties": { - "value": "[tryGet(coalesce(parameters('computes'), createArray())[copyIndex()], 'properties')]" - } - }, - "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.33.93.31351", - "templateHash": "6400587070695625658" - }, - "name": "Machine Learning Services Workspaces Computes", - "description": "This module deploys a Machine Learning Services Workspaces Compute.\n\nAttaching a compute is not idempotent and will fail in case you try to redeploy over an existing compute in AML (see parameter `deployCompute`)." - }, - "definitions": { - "managedIdentityAllType": { - "type": "object", - "properties": { - "systemAssigned": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Enables system assigned managed identity on the resource." - } - }, - "userAssignedResourceIds": { - "type": "array", - "items": { - "type": "string" - }, - "nullable": true, - "metadata": { - "description": "Optional. The resource ID(s) to assign to the resource. Required if a user assigned identity is used for encryption." - } - } - }, - "metadata": { - "description": "An AVM-aligned type for a managed identity configuration. To be used if both a system-assigned & user-assigned identities are supported by the resource provider.", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" - } - } - } - }, - "parameters": { - "machineLearningWorkspaceName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent Machine Learning Workspace. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", - "minLength": 2, - "maxLength": 16, - "metadata": { - "description": "Required. Name of the compute." - } - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]", - "metadata": { - "description": "Optional. Specifies the location of the resource." - } - }, - "sku": { - "type": "string", - "nullable": true, - "allowedValues": [ - "Basic", - "Free", - "Premium", - "Standard" - ], - "metadata": { - "description": "Optional. Specifies the sku, also referred as \"edition\". Required for creating a compute resource." - } - }, - "tags": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Contains resource tags defined as key-value pairs. Ignored when attaching a compute resource, i.e. when you provide a resource ID." - } - }, - "deployCompute": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Optional. Flag to specify whether to deploy the compute. Required only for attach (i.e. providing a resource ID), as in this case the operation is not idempotent, i.e. a second deployment will fail. Therefore, this flag needs to be set to \"false\" as long as the compute resource exists." - } - }, - "computeLocation": { - "type": "string", - "defaultValue": "[resourceGroup().location]", - "metadata": { - "description": "Optional. Location for the underlying compute. Ignored when attaching a compute resource, i.e. when you provide a resource ID." - } - }, - "description": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The description of the Machine Learning compute." - } - }, - "disableLocalAuth": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "Optional. Opt-out of local authentication and ensure customers can use only MSI and AAD exclusively for authentication." - } - }, - "resourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. ARM resource ID of the underlying compute." - } - }, - "computeType": { - "type": "string", - "allowedValues": [ - "AKS", - "AmlCompute", - "ComputeInstance", - "Databricks", - "DataFactory", - "DataLakeAnalytics", - "HDInsight", - "Kubernetes", - "SynapseSpark", - "VirtualMachine" - ], - "metadata": { - "description": "Required. Set the object type." - } - }, - "properties": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The properties of the compute. Will be ignored in case \"resourceId\" is set." - } - }, - "managedIdentities": { - "$ref": "#/definitions/managedIdentityAllType", - "nullable": true, - "metadata": { - "description": "Optional. The managed identity definition for this resource." - } - } - }, - "variables": { - "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", - "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned,UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', null())), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]" - }, - "resources": { - "machineLearningWorkspace": { - "existing": true, - "type": "Microsoft.MachineLearningServices/workspaces", - "apiVersion": "2022-10-01", - "name": "[parameters('machineLearningWorkspaceName')]" - }, - "compute": { - "condition": "[equals(parameters('deployCompute'), true())]", - "type": "Microsoft.MachineLearningServices/workspaces/computes", - "apiVersion": "2022-10-01", - "name": "[format('{0}/{1}', parameters('machineLearningWorkspaceName'), parameters('name'))]", - "location": "[parameters('location')]", - "tags": "[if(empty(parameters('resourceId')), parameters('tags'), null())]", - "sku": "[if(empty(parameters('resourceId')), createObject('name', parameters('sku'), 'tier', parameters('sku')), null())]", - "identity": "[if(empty(parameters('resourceId')), variables('identity'), null())]", - "properties": "[union(createObject('description', parameters('description'), 'disableLocalAuth', parameters('disableLocalAuth'), 'computeType', parameters('computeType')), if(not(empty(parameters('resourceId'))), createObject('resourceId', parameters('resourceId')), createObject('computeLocation', parameters('computeLocation'), 'properties', parameters('properties'))))]" - } - }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the compute." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the compute." - }, - "value": "[resourceId('Microsoft.MachineLearningServices/workspaces/computes', parameters('machineLearningWorkspaceName'), parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group the compute was deployed into." - }, - "value": "[resourceGroup().name]" - }, - "systemAssignedMIPrincipalId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "The principal ID of the system assigned identity." - }, - "value": "[tryGet(tryGet(reference('compute', '2022-10-01', 'full'), 'identity'), 'principalId')]" - }, - "location": { - "type": "string", - "metadata": { - "description": "The location the resource was deployed into." - }, - "value": "[reference('compute', '2022-10-01', 'full').location]" - } - } - } - }, - "dependsOn": [ - "workspace", - "workspace_privateEndpoints" - ] - }, - "workspace_connections": { - "copy": { - "name": "workspace_connections", - "count": "[length(parameters('connections'))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-{1}-connection', parameters('name'), parameters('connections')[copyIndex()].name)]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "machineLearningWorkspaceName": { - "value": "[parameters('name')]" - }, - "name": { - "value": "[parameters('connections')[copyIndex()].name]" - }, - "category": { - "value": "[parameters('connections')[copyIndex()].category]" - }, - "expiryTime": { - "value": "[tryGet(parameters('connections')[copyIndex()], 'expiryTime')]" - }, - "isSharedToAll": { - "value": "[tryGet(parameters('connections')[copyIndex()], 'isSharedToAll')]" - }, - "metadata": { - "value": "[tryGet(parameters('connections')[copyIndex()], 'metadata')]" - }, - "sharedUserList": { - "value": "[tryGet(parameters('connections')[copyIndex()], 'sharedUserList')]" - }, - "target": { - "value": "[parameters('connections')[copyIndex()].target]" - }, - "value": { - "value": "[tryGet(parameters('connections')[copyIndex()], 'value')]" - }, - "connectionProperties": { - "value": "[parameters('connections')[copyIndex()].connectionProperties]" - } - }, - "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.33.93.31351", - "templateHash": "10473859406464148345" - }, - "name": "Machine Learning Services Workspaces Connections", - "description": "This module creates a connection in a Machine Learning Services workspace." - }, - "definitions": { - "metadataType": { - "type": "object", - "properties": {}, - "additionalProperties": { - "type": "string", - "metadata": { - "description": "Required. The metadata key-value pairs." - } - } - }, - "categoryType": { - "type": "string", - "allowedValues": [ - "ADLSGen2", - "AIServices", - "AmazonMws", - "AmazonRdsForOracle", - "AmazonRdsForSqlServer", - "AmazonRedshift", - "AmazonS3Compatible", - "ApiKey", - "AzureBlob", - "AzureDataExplorer", - "AzureDatabricksDeltaLake", - "AzureMariaDb", - "AzureMySqlDb", - "AzureOneLake", - "AzureOpenAI", - "AzurePostgresDb", - "AzureSqlDb", - "AzureSqlMi", - "AzureSynapseAnalytics", - "AzureTableStorage", - "BingLLMSearch", - "Cassandra", - "CognitiveSearch", - "CognitiveService", - "Concur", - "ContainerRegistry", - "CosmosDb", - "CosmosDbMongoDbApi", - "Couchbase", - "CustomKeys", - "Db2", - "Drill", - "Dynamics", - "DynamicsAx", - "DynamicsCrm", - "Eloqua", - "FileServer", - "FtpServer", - "GenericContainerRegistry", - "GenericHttp", - "GenericRest", - "Git", - "GoogleAdWords", - "GoogleBigQuery", - "GoogleCloudStorage", - "Greenplum", - "Hbase", - "Hdfs", - "Hive", - "Hubspot", - "Impala", - "Informix", - "Jira", - "Magento", - "MariaDb", - "Marketo", - "MicrosoftAccess", - "MongoDbAtlas", - "MongoDbV2", - "MySql", - "Netezza", - "ODataRest", - "Odbc", - "Office365", - "OpenAI", - "Oracle", - "OracleCloudStorage", - "OracleServiceCloud", - "PayPal", - "Phoenix", - "PostgreSql", - "Presto", - "PythonFeed", - "QuickBooks", - "Redis", - "Responsys", - "S3", - "Salesforce", - "SalesforceMarketingCloud", - "SalesforceServiceCloud", - "SapBw", - "SapCloudForCustomer", - "SapEcc", - "SapHana", - "SapOpenHub", - "SapTable", - "Serp", - "Serverless", - "ServiceNow", - "Sftp", - "SharePointOnlineList", - "Shopify", - "Snowflake", - "Spark", - "SqlServer", - "Square", - "Sybase", - "Teradata", - "Vertica", - "WebTable", - "Xero", - "Zoho" - ], - "metadata": { - "__bicep_export!": true - } - }, - "aadAuthTypeWorkspaceConnectionPropertyType": { - "type": "object", - "properties": { - "authType": { - "type": "string", - "allowedValues": [ - "AAD" - ], - "metadata": { - "description": "Required. The authentication type of the connection target." - } - } - } - }, - "accessKeyAuthTypeWorkspaceConnectionPropertyType": { - "type": "object", - "properties": { - "authType": { - "type": "string", - "allowedValues": [ - "AccessKey" - ], - "metadata": { - "description": "Required. The authentication type of the connection target." - } - }, - "credentials": { - "$ref": "#/definitions/workspaceConnectionAccessKeyType", - "metadata": { - "description": "Required. The credentials for the connection." - } - } - } - }, - "accountKeyAuthTypeWorkspaceConnectionPropertyType": { - "type": "object", - "properties": { - "authType": { - "type": "string", - "allowedValues": [ - "AccountKey" - ], - "metadata": { - "description": "Required. The authentication type of the connection target." - } - }, - "credentials": { - "$ref": "#/definitions/workspaceConnectionAccountKey", - "metadata": { - "description": "Required. The credentials for the connection." - } - } - } - }, - "apiKeyAuthWorkspaceConnectionPropertyType": { - "type": "object", - "properties": { - "authType": { - "type": "string", - "allowedValues": [ - "ApiKey" - ], - "metadata": { - "description": "Required. The authentication type of the connection target." - } - }, - "credentials": { - "$ref": "#/definitions/workspaceConnectionApiKeyType", - "metadata": { - "description": "Required. The credentials for the connection." - } - } - } - }, - "customKeysWorkspaceConnectionPropertyType": { - "type": "object", - "properties": { - "authType": { - "type": "string", - "allowedValues": [ - "CustomKeys" - ], - "metadata": { - "description": "Required. The authentication type of the connection target." - } - }, - "credentials": { - "$ref": "#/definitions/customKeysType", - "metadata": { - "description": "Required. The credentials for the connection." - } - } - } - }, - "managedIdentityAuthTypeWorkspaceConnectionPropertyType": { - "type": "object", - "properties": { - "authType": { - "type": "string", - "allowedValues": [ - "ManagedIdentity" - ], - "metadata": { - "description": "Required. The authentication type of the connection target." - } - }, - "credentials": { - "$ref": "#/definitions/workspaceConnectionManagedIdentityType", - "metadata": { - "description": "Required. The credentials for the connection." - } - } - } - }, - "noneAuthTypeWorkspaceConnectionPropertyType": { - "type": "object", - "properties": { - "authType": { - "type": "string", - "allowedValues": [ - "None" - ], - "metadata": { - "description": "Required. The authentication type of the connection target." - } - } - } - }, - "oauth2AuthTypeWorkspaceConnectionPropertyType": { - "type": "object", - "properties": { - "authType": { - "type": "string", - "allowedValues": [ - "OAuth2" - ], - "metadata": { - "description": "Required. The authentication type of the connection target." - } - }, - "credentials": { - "$ref": "#/definitions/workspaceConnectionOAuth2Type", - "metadata": { - "description": "Required. The credentials for the connection." - } - } - } - }, - "patAuthTypeWorkspaceConnectionPropertyType": { - "type": "object", - "properties": { - "authType": { - "type": "string", - "allowedValues": [ - "PAT" - ], - "metadata": { - "description": "Required. The authentication type of the connection target." - } - }, - "credentials": { - "$ref": "#/definitions/workspaceConnectionPersonalAccessTokenType", - "metadata": { - "description": "Required. The credentials for the connection." - } - } - } - }, - "sasAuthTypeWorkspaceConnectionPropertyType": { - "type": "object", - "properties": { - "authType": { - "type": "string", - "allowedValues": [ - "SAS" - ], - "metadata": { - "description": "Required. The authentication type of the connection target." - } - }, - "credentials": { - "$ref": "#/definitions/workspaceConnectionSharedAccessSignatureType", - "metadata": { - "description": "Required. The credentials for the connection." - } - } - } - }, - "servicePrincipalAuthTypeWorkspaceConnectionPropertyType": { - "type": "object", - "properties": { - "authType": { - "type": "string", - "allowedValues": [ - "ServicePrincipal" - ], - "metadata": { - "description": "Required. The authentication type of the connection target." - } - }, - "credentials": { - "$ref": "#/definitions/workspaceConnectionServicePrincipalType", - "metadata": { - "description": "Required. The credentials for the connection." - } - } - } - }, - "usernamePasswordAuthTypeWorkspaceConnectionPropertyType": { - "type": "object", - "properties": { - "authType": { - "type": "string", - "allowedValues": [ - "UsernamePassword" - ], - "metadata": { - "description": "Required. The authentication type of the connection target." - } - }, - "credentials": { - "$ref": "#/definitions/workspaceConnectionUsernamePasswordType", - "metadata": { - "description": "Required. The credentials for the connection." - } - } - } - }, - "customKeysType": { - "type": "object", - "properties": { - "keys": { - "type": "object", - "properties": {}, - "additionalProperties": { - "type": "string", - "metadata": { - "description": "Required. Key-value pairs for the custom keys." - } - }, - "metadata": { - "description": "Required. The custom keys for the connection." - } - } - } - }, - "workspaceConnectionAccessKeyType": { - "type": "object", - "properties": { - "accessKeyId": { - "type": "string", - "metadata": { - "description": "Required. The connection access key ID." - } - }, - "secretAccessKey": { - "type": "string", - "metadata": { - "description": "Required. The connection secret access key." - } - } - } - }, - "workspaceConnectionAccountKey": { - "type": "object", - "properties": { - "key": { - "type": "string", - "metadata": { - "description": "Required. The connection key." - } - } - } - }, - "workspaceConnectionApiKeyType": { - "type": "object", - "properties": { - "key": { - "type": "string", - "metadata": { - "description": "Required. The connection API key." - } - } - } - }, - "workspaceConnectionManagedIdentityType": { - "type": "object", - "properties": { - "clientId": { - "type": "string", - "metadata": { - "description": "Required. The connection managed identity ID." - } - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "Required. The connection managed identity resource ID." - } - } - } - }, - "workspaceConnectionOAuth2Type": { - "type": "object", - "properties": { - "authUrl": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Conditional. The connection auth URL. Required by Concur connection category." - } - }, - "clientId": { - "type": "string", - "minLength": 36, - "maxLength": 36, - "metadata": { - "description": "Required. The connection client ID in the format of UUID." - } - }, - "clientSecret": { - "type": "string", - "metadata": { - "description": "Required. The connection client secret." - } - }, - "developerToken": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Conditional. The connection developer token. Required by GoogleAdWords connection category." - } - }, - "password": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Conditional. The connection password. Required by Concur and ServiceNow connection categories where AccessToken grant type is 'Password'." - } - }, - "refreshToken": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Conditional. The connection refresh token. Required by GoogleBigQuery, GoogleAdWords, Hubspot, QuickBooks, Square, Xero and Zoho connection categories." - } - }, - "tenantId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Required. The connection tenant ID. Required by QuickBooks and Xero connection categories." - } - }, - "username": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Conditional. The connection username. Required by Concur and ServiceNow connection categories where AccessToken grant type is 'Password'." - } - } - } - }, - "workspaceConnectionPersonalAccessTokenType": { - "type": "object", - "properties": { - "pat": { - "type": "string", - "metadata": { - "description": "Required. The connection personal access token." - } - } - } - }, - "workspaceConnectionSharedAccessSignatureType": { - "type": "object", - "properties": { - "sas": { - "type": "string", - "metadata": { - "description": "Required. The connection SAS token." - } - } - } - }, - "workspaceConnectionServicePrincipalType": { - "type": "object", - "properties": { - "clientId": { - "type": "string", - "metadata": { - "description": "Required. The connection client ID." - } - }, - "clientSecret": { - "type": "string", - "metadata": { - "description": "Required. The connection client secret." - } - }, - "tenantId": { - "type": "string", - "metadata": { - "description": "Required. The connection tenant ID." - } - } - } - }, - "workspaceConnectionUsernamePasswordType": { - "type": "object", - "properties": { - "password": { - "type": "string", - "metadata": { - "description": "Required. The connection password." - } - }, - "securityToken": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Conditional. The connection security token. Required by connections like SalesForce for extra security in addition to 'UsernamePassword'." - } - }, - "username": { - "type": "string", - "metadata": { - "description": "Required. The connection username." - } - } - } - }, - "connectionPropertyType": { - "type": "secureObject", - "discriminator": { - "propertyName": "authType", - "mapping": { - "AAD": { - "$ref": "#/definitions/aadAuthTypeWorkspaceConnectionPropertyType" - }, - "AccessKey": { - "$ref": "#/definitions/accessKeyAuthTypeWorkspaceConnectionPropertyType" - }, - "ApiKey": { - "$ref": "#/definitions/apiKeyAuthWorkspaceConnectionPropertyType" - }, - "CustomKeys": { - "$ref": "#/definitions/customKeysWorkspaceConnectionPropertyType" - }, - "ManagedIdentity": { - "$ref": "#/definitions/managedIdentityAuthTypeWorkspaceConnectionPropertyType" - }, - "None": { - "$ref": "#/definitions/noneAuthTypeWorkspaceConnectionPropertyType" - }, - "OAuth2": { - "$ref": "#/definitions/oauth2AuthTypeWorkspaceConnectionPropertyType" - }, - "PAT": { - "$ref": "#/definitions/patAuthTypeWorkspaceConnectionPropertyType" - }, - "SAS": { - "$ref": "#/definitions/sasAuthTypeWorkspaceConnectionPropertyType" - }, - "ServicePrincipal": { - "$ref": "#/definitions/servicePrincipalAuthTypeWorkspaceConnectionPropertyType" - }, - "UsernamePassword": { - "$ref": "#/definitions/usernamePasswordAuthTypeWorkspaceConnectionPropertyType" - } - } - }, - "metadata": { - "__bicep_export!": true - } - } - }, - "parameters": { - "name": { - "type": "string", - "metadata": { - "description": "Required. Name of the connection to create." - } - }, - "machineLearningWorkspaceName": { - "type": "string", - "metadata": { - "description": "Required. The name of the parent Machine Learning Workspace. Required if the template is used in a standalone deployment." - } - }, - "category": { - "$ref": "#/definitions/categoryType", - "metadata": { - "description": "Required. Category of the connection." - } - }, - "expiryTime": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The expiry time of the connection." - } - }, - "isSharedToAll": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Indicates whether the connection is shared to all users in the workspace." - } - }, - "metadata": { - "$ref": "#/definitions/metadataType", - "defaultValue": {}, - "metadata": { - "description": "Optional. User metadata for the connection." - } - }, - "sharedUserList": { - "type": "array", - "items": { - "type": "string" - }, - "nullable": true, - "metadata": { - "description": "Optional. The shared user list of the connection." - } - }, - "target": { - "type": "string", - "metadata": { - "description": "Required. The target of the connection." - } - }, - "value": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Value details of the workspace connection." - } - }, - "connectionProperties": { - "$ref": "#/definitions/connectionPropertyType", - "metadata": { - "description": "Required. The properties of the connection, specific to the auth type." - } - } - }, - "resources": { - "machineLearningWorkspace": { - "existing": true, - "type": "Microsoft.MachineLearningServices/workspaces", - "apiVersion": "2022-10-01", - "name": "[parameters('machineLearningWorkspaceName')]" - }, - "connection": { - "type": "Microsoft.MachineLearningServices/workspaces/connections", - "apiVersion": "2024-04-01", - "name": "[format('{0}/{1}', parameters('machineLearningWorkspaceName'), parameters('name'))]", - "properties": "[union(createObject('category', parameters('category'), 'expiryTime', parameters('expiryTime'), 'isSharedToAll', parameters('isSharedToAll'), 'metadata', parameters('metadata'), 'sharedUserList', parameters('sharedUserList'), 'target', parameters('target'), 'value', parameters('value')), parameters('connectionProperties'))]" - } - }, - "outputs": { - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the connection." - }, - "value": "[resourceId('Microsoft.MachineLearningServices/workspaces/connections', parameters('machineLearningWorkspaceName'), parameters('name'))]" - }, - "name": { - "type": "string", - "metadata": { - "description": "The name of the connection." - }, - "value": "[parameters('name')]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The name of the resource group the connection was created in." - }, - "value": "[resourceGroup().name]" - } - } - } - }, - "dependsOn": [ - "workspace" - ] - }, - "workspace_privateEndpoints": { - "copy": { - "name": "workspace_privateEndpoints", - "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-workspace-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "resourceGroup": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupName'), '')]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'name'), format('pep-{0}-{1}-{2}', last(split(resourceId('Microsoft.MachineLearningServices/workspaces', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'amlworkspace'), copyIndex()))]" - }, - "privateLinkServiceConnections": "[if(not(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true())), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.MachineLearningServices/workspaces', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'amlworkspace'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.MachineLearningServices/workspaces', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'amlworkspace')))))), createObject('value', null()))]", - "manualPrivateLinkServiceConnections": "[if(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true()), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.MachineLearningServices/workspaces', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'amlworkspace'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.MachineLearningServices/workspaces', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'amlworkspace')), 'requestMessage', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualConnectionRequestMessage'), 'Manual approval required.'))))), createObject('value', null()))]", - "subnetResourceId": { - "value": "[coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId]" - }, - "enableTelemetry": { - "value": "[variables('enableReferencedModulesTelemetry')]" - }, - "location": { - "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'location'), reference(split(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location)]" - }, - "lock": { - "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" - }, - "privateDnsZoneGroup": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroup')]" - }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'roleAssignments')]" - }, - "tags": { - "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" - }, - "customDnsConfigs": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customDnsConfigs')]" - }, - "ipConfigurations": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'ipConfigurations')]" - }, - "applicationSecurityGroupResourceIds": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'applicationSecurityGroupResourceIds')]" - }, - "customNetworkInterfaceName": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customNetworkInterfaceName')]" - } - }, - "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.29.47.4906", - "templateHash": "3308873178893851812" - }, - "name": "Private Endpoints", - "description": "This module deploys a Private Endpoint.", - "owner": "Azure/module-maintainers" - }, - "definitions": { - "privateDnsZoneGroupType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the Private DNS Zone Group." - } - }, - "privateDnsZoneGroupConfigs": { - "type": "array", - "items": { - "$ref": "#/definitions/privateDnsZoneGroupConfigType" - }, - "metadata": { - "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." - } - } - } - }, - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } - } - } - }, - "nullable": true - }, - "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." - } - } - }, - "nullable": true - }, - "ipConfigurationsType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the resource that is unique within a resource group." - } - }, - "properties": { - "type": "object", - "properties": { - "groupId": { - "type": "string", - "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." - } - }, - "memberName": { - "type": "string", - "metadata": { - "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." - } - }, - "privateIPAddress": { - "type": "string", - "metadata": { - "description": "Required. A private IP address obtained from the private endpoint's subnet." - } - } - }, - "metadata": { - "description": "Required. Properties of private endpoint IP configurations." - } - } - } - }, - "nullable": true - }, - "manualPrivateLinkServiceConnectionsType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the private link service connection." - } - }, - "properties": { - "type": "object", - "properties": { - "groupIds": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." - } - }, - "privateLinkServiceId": { - "type": "string", - "metadata": { - "description": "Required. The resource id of private link service." - } - }, - "requestMessage": { - "type": "string", - "metadata": { - "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." - } - } - }, - "metadata": { - "description": "Required. Properties of private link service connection." - } - } - } - }, - "nullable": true - }, - "privateLinkServiceConnectionsType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the private link service connection." - } - }, - "properties": { - "type": "object", - "properties": { - "groupIds": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." - } - }, - "privateLinkServiceId": { - "type": "string", - "metadata": { - "description": "Required. The resource id of private link service." - } - }, - "requestMessage": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." - } - } - }, - "metadata": { - "description": "Required. Properties of private link service connection." - } - } - } - }, - "nullable": true - }, - "customDnsConfigType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "fqdn": { - "type": "string", - "metadata": { - "description": "Required. Fqdn that resolves to private endpoint IP address." - } - }, - "ipAddresses": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. A list of private IP addresses of the private endpoint." - } - } - } - }, - "nullable": true - }, - "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_imported_from!": { - "sourceTemplate": "private-dns-zone-group/main.bicep" - } - } - } - }, - "parameters": { - "name": { - "type": "string", - "metadata": { - "description": "Required. Name of the private endpoint resource to create." - } - }, - "subnetResourceId": { - "type": "string", - "metadata": { - "description": "Required. Resource ID of the subnet where the endpoint needs to be created." - } - }, - "applicationSecurityGroupResourceIds": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. Application security groups in which the private endpoint IP configuration is included." - } - }, - "customNetworkInterfaceName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The custom name of the network interface attached to the private endpoint." - } - }, - "ipConfigurations": { - "$ref": "#/definitions/ipConfigurationsType", - "metadata": { - "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." - } - }, - "privateDnsZoneGroup": { - "$ref": "#/definitions/privateDnsZoneGroupType", - "nullable": true, - "metadata": { - "description": "Optional. The private DNS zone group to configure for the private endpoint." - } - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]", - "metadata": { - "description": "Optional. Location for all Resources." - } - }, - "lock": { - "$ref": "#/definitions/lockType", - "metadata": { - "description": "Optional. The lock settings of the service." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "tags": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." - } - }, - "customDnsConfigs": { - "$ref": "#/definitions/customDnsConfigType", - "metadata": { - "description": "Optional. Custom DNS configurations." - } - }, - "manualPrivateLinkServiceConnections": { - "$ref": "#/definitions/manualPrivateLinkServiceConnectionsType", - "metadata": { - "description": "Optional. 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." - } - }, - "privateLinkServiceConnections": { - "$ref": "#/definitions/privateLinkServiceConnectionsType", - "metadata": { - "description": "Optional. A grouping of information about the connection to the remote resource." - } - }, - "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 (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" - } - }, - "resources": { - "avmTelemetry": { - "condition": "[parameters('enableTelemetry')]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2024-03-01", - "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.7.0', '.', '-'), 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": "2023-11-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": "[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": "2022-09-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.29.47.4906", - "templateHash": "5805178546717255803" - }, - "name": "Private Endpoint Private DNS Zone Groups", - "description": "This module deploys a Private Endpoint Private DNS Zone Group.", - "owner": "Azure/module-maintainers" - }, - "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 - } - } - }, - "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." - } - } - }, - "variables": { - "copy": [ - { - "name": "privateDnsZoneConfigsVar", - "count": "[length(parameters('privateDnsZoneConfigs'))]", - "input": { - "name": "[coalesce(tryGet(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')], 'name'), last(split(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId, '/')))]", - "properties": { - "privateDnsZoneId": "[parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId]" - } - } - } - ] - }, - "resources": { - "privateEndpoint": { - "existing": true, - "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2023-11-01", - "name": "[parameters('privateEndpointName')]" - }, - "privateDnsZoneGroup": { - "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2023-11-01", - "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", - "properties": { - "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" - }, - "dependsOn": [ - "privateEndpoint" - ] - } - }, - "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', '2023-11-01', 'full').location]" - }, - "customDnsConfig": { - "$ref": "#/definitions/customDnsConfigType", - "metadata": { - "description": "The custom DNS configurations of the private endpoint." - }, - "value": "[reference('privateEndpoint').customDnsConfigs]" - }, - "groupId": { - "type": "string", - "metadata": { - "description": "The group Id for the private endpoint Group." - }, - "value": "[if(and(not(empty(reference('privateEndpoint').manualPrivateLinkServiceConnections)), greater(length(tryGet(reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties, 'groupIds')), 0)), coalesce(tryGet(reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties, 'groupIds', 0), ''), if(and(not(empty(reference('privateEndpoint').privateLinkServiceConnections)), greater(length(tryGet(reference('privateEndpoint').privateLinkServiceConnections[0].properties, 'groupIds')), 0)), coalesce(tryGet(reference('privateEndpoint').privateLinkServiceConnections[0].properties, 'groupIds', 0), ''), ''))]" - } - } + "type": "string", + "defaultValue": "MicrosoftWindowsServer", + "metadata": { + "description": "Specifies the image publisher of the disk image used to create the virtual machine." + } + }, + "imageOffer": { + "type": "string", + "defaultValue": "WindowsServer", + "metadata": { + "description": "Specifies the offer of the platform image or marketplace image used to create the virtual machine." + } + }, + "imageSku": { + "type": "string", + "defaultValue": "2022-datacenter-azure-edition", + "metadata": { + "description": "Specifies the image version for the virtual machine." + } + }, + "authenticationType": { + "type": "string", + "defaultValue": "password", + "allowedValues": [ + "sshPublicKey", + "password" + ], + "metadata": { + "description": "Specifies the type of authentication when accessing the Virtual Machine. SSH key is recommended." + } + }, + "vmAdminUsername": { + "type": "string", + "metadata": { + "description": "Specifies the name of the administrator account of the virtual machine." + } + }, + "vmAdminPasswordOrKey": { + "type": "securestring", + "metadata": { + "description": "Specifies the SSH Key or password for the virtual machine. SSH key is recommended." + } + }, + "diskStorageAccountType": { + "type": "string", + "defaultValue": "Premium_LRS", + "allowedValues": [ + "Premium_LRS", + "StandardSSD_LRS", + "Standard_LRS", + "UltraSSD_LRS" + ], + "metadata": { + "description": "Specifies the storage account type for OS and data disk." + } + }, + "numDataDisks": { + "type": "int", + "defaultValue": 1, + "minValue": 0, + "maxValue": 64, + "metadata": { + "description": "Specifies the number of data disks of the virtual machine." + } + }, + "osDiskSize": { + "type": "int", + "defaultValue": 128, + "metadata": { + "description": "Specifies the size in GB of the OS disk of the VM." + } + }, + "dataDiskSize": { + "type": "int", + "defaultValue": 50, + "metadata": { + "description": "Specifies the size in GB of the OS disk of the virtual machine." + } + }, + "dataDiskCaching": { + "type": "string", + "defaultValue": "ReadWrite", + "metadata": { + "description": "Specifies the caching requirements for the data disks." + } + }, + "enableMicrosoftEntraIdAuth": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Specifies whether enabling Microsoft Entra ID authentication on the virtual machine." + } + }, + "enableAcceleratedNetworking": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Specifies whether enabling accelerated networking on the virtual machine." + } + }, + "vmNicName": { + "type": "string", + "metadata": { + "description": "Specifies the name of the network interface of the virtual machine." + } + }, + "userObjectId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Specifies the object id of a Microsoft Entra ID user. In general, this the object id of the system administrator who deploys the Azure resources." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Specifies the location." + } + }, + "workspaceId": { + "type": "string", + "metadata": { + "description": "Specifies the resource id of the Log Analytics workspace." + } + }, + "tags": { + "type": "object", + "metadata": { + "description": "Specifies the resource tags." + } + } + }, + "variables": { + "randomString": "[uniqueString(resourceGroup().id, parameters('vmName'), parameters('vmAdminPasswordOrKey'))]", + "adminPassword": "[if(less(length(parameters('vmAdminPasswordOrKey')), 8), format('{0}{1}', parameters('vmAdminPasswordOrKey'), take(variables('randomString'), 12)), parameters('vmAdminPasswordOrKey'))]", + "linuxConfiguration": { + "disablePasswordAuthentication": true, + "ssh": { + "publicKeys": [ + { + "path": "[format('/home/{0}/.ssh/authorized_keys', parameters('vmAdminUsername'))]", + "keyData": "[variables('adminPassword')]" + } + ] + }, + "provisionVMAgent": true + } + }, + "resources": [ + { + "type": "Microsoft.Network/networkInterfaces", + "apiVersion": "2021-08-01", + "name": "[parameters('vmNicName')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "enableAcceleratedNetworking": "[parameters('enableAcceleratedNetworking')]", + "ipConfigurations": [ + { + "name": "ipconfig1", + "properties": { + "privateIPAllocationMethod": "Dynamic", + "subnet": { + "id": "[parameters('vmSubnetId')]" + } + } + } + ] + } + }, + { + "type": "Microsoft.Compute/virtualMachines", + "apiVersion": "2021-11-01", + "name": "[parameters('vmName')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "hardwareProfile": { + "vmSize": "[parameters('vmSize')]" + }, + "osProfile": { + "computerName": "[take(parameters('vmName'), 15)]", + "adminUsername": "[parameters('vmAdminUsername')]", + "adminPassword": "[variables('adminPassword')]", + "linuxConfiguration": "[if(equals(parameters('authenticationType'), 'password'), null(), variables('linuxConfiguration'))]" + }, + "storageProfile": { + "copy": [ + { + "name": "dataDisks", + "count": "[length(range(0, parameters('numDataDisks')))]", + "input": { + "caching": "[parameters('dataDiskCaching')]", + "diskSizeGB": "[parameters('dataDiskSize')]", + "lun": "[range(0, parameters('numDataDisks'))[copyIndex('dataDisks')]]", + "name": "[format('{0}-DataDisk{1}', parameters('vmName'), range(0, parameters('numDataDisks'))[copyIndex('dataDisks')])]", + "createOption": "Empty", + "managedDisk": { + "storageAccountType": "[parameters('diskStorageAccountType')]" } - }, - "dependsOn": [ - "workspace" - ] + } } + ], + "imageReference": { + "publisher": "[parameters('imagePublisher')]", + "offer": "[parameters('imageOffer')]", + "sku": "[parameters('imageSku')]", + "version": "latest" }, - "outputs": { - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the machine learning service." - }, - "value": "[resourceId('Microsoft.MachineLearningServices/workspaces', parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group the machine learning service was deployed into." - }, - "value": "[resourceGroup().name]" - }, - "name": { - "type": "string", - "metadata": { - "description": "The name of the machine learning service." - }, - "value": "[parameters('name')]" - }, - "systemAssignedMIPrincipalId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "The principal ID of the system assigned identity." - }, - "value": "[tryGet(tryGet(reference('workspace', '2024-04-01-preview', 'full'), 'identity'), 'principalId')]" - }, - "location": { - "type": "string", - "metadata": { - "description": "The location the resource was deployed into." - }, - "value": "[reference('workspace', '2024-04-01-preview', 'full').location]" + "osDisk": { + "name": "[format('{0}_OSDisk', parameters('vmName'))]", + "caching": "ReadWrite", + "createOption": "FromImage", + "diskSizeGB": "[parameters('osDiskSize')]", + "managedDisk": { + "storageAccountType": "[parameters('diskStorageAccountType')]" + } + } + }, + "networkProfile": { + "networkInterfaces": [ + { + "id": "[resourceId('Microsoft.Network/networkInterfaces', parameters('vmNicName'))]" + } + ] + }, + "diagnosticsProfile": { + "bootDiagnostics": { + "enabled": true, + "storageUri": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('storageAccountResourceGroup')), 'Microsoft.Storage/storageAccounts', parameters('storageAccountName')), '2021-09-01').primaryEndpoints.blob]" + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/networkInterfaces', parameters('vmNicName'))]" + ] + }, + { + "type": "Microsoft.Compute/virtualMachines/extensions", + "apiVersion": "2023-09-01", + "name": "[format('{0}/{1}', parameters('vmName'), 'DependencyAgentWindows')]", + "location": "[parameters('location')]", + "properties": { + "publisher": "Microsoft.Azure.Monitoring.DependencyAgent", + "type": "DependencyAgentWindows", + "typeHandlerVersion": "9.4", + "autoUpgradeMinorVersion": true, + "enableAutomaticUpgrade": true + }, + "dependsOn": [ + "[resourceId('Microsoft.Compute/virtualMachines', parameters('vmName'))]" + ] + }, + { + "type": "Microsoft.Compute/virtualMachines/extensions", + "apiVersion": "2023-09-01", + "name": "[format('{0}/{1}', parameters('vmName'), 'AzureMonitorWindowsAgent')]", + "location": "[parameters('location')]", + "properties": { + "publisher": "Microsoft.Azure.Monitor", + "type": "AzureMonitorWindowsAgent", + "typeHandlerVersion": "1.0", + "autoUpgradeMinorVersion": true, + "enableAutomaticUpgrade": true + }, + "dependsOn": [ + "[resourceId('Microsoft.Compute/virtualMachines/extensions', parameters('vmName'), 'DependencyAgentWindows')]", + "[resourceId('Microsoft.Compute/virtualMachines', parameters('vmName'))]" + ] + }, + { + "condition": "[parameters('enableMicrosoftEntraIdAuth')]", + "type": "Microsoft.Compute/virtualMachines/extensions", + "apiVersion": "2023-09-01", + "name": "[format('{0}/{1}', parameters('vmName'), 'AADLoginForWindows')]", + "location": "[parameters('location')]", + "properties": { + "publisher": "Microsoft.Azure.ActiveDirectory", + "type": "AADLoginForWindows", + "typeHandlerVersion": "1.0", + "autoUpgradeMinorVersion": false, + "enableAutomaticUpgrade": false + }, + "dependsOn": [ + "[resourceId('Microsoft.Compute/virtualMachines/extensions', parameters('vmName'), 'AzureMonitorWindowsAgent')]", + "[resourceId('Microsoft.Compute/virtualMachines', parameters('vmName'))]" + ] + }, + { + "type": "Microsoft.Insights/dataCollectionRules", + "apiVersion": "2022-06-01", + "name": "DCR-Win-Event-Logs-to-LAW", + "location": "[parameters('location')]", + "kind": "Windows", + "properties": { + "dataFlows": [ + { + "destinations": [ + "logAnalytics" + ], + "streams": [ + "Microsoft-Event" + ] + } + ], + "dataSources": { + "windowsEventLogs": [ + { + "streams": [ + "Microsoft-Event" + ], + "xPathQueries": [ + "Application!*[System[(Level=1 or Level=2 or Level=3 or or Level=0) ]]", + "Security!*[System[(band(Keywords,13510798882111488))]]", + "System!*[System[(Level=1 or Level=2 or Level=3 or or Level=0)]]" + ], + "name": "eventLogsDataSource" + } + ] + }, + "description": "Collect Windows Event Logs and send to Azure Monitor Logs", + "destinations": { + "logAnalytics": [ + { + "name": "logAnalytics", + "workspaceResourceId": "[parameters('workspaceId')]" } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Compute/virtualMachines/extensions', parameters('vmName'), 'AADLoginForWindows')]" + ] + }, + { + "type": "Microsoft.Insights/dataCollectionRules", + "apiVersion": "2022-06-01", + "name": "DCR-Win-Perf-to-LAW", + "location": "[parameters('location')]", + "kind": "Windows", + "properties": { + "dataFlows": [ + { + "destinations": [ + "logAnalytics" + ], + "streams": [ + "Microsoft-Perf" + ] } + ], + "dataSources": { + "performanceCounters": [ + { + "counterSpecifiers": [ + "\\Processor Information(_Total)\\% Processor Time", + "\\Processor Information(_Total)\\% Privileged Time", + "\\Processor Information(_Total)\\% User Time", + "\\Processor Information(_Total)\\Processor Frequency", + "\\System\\Processes", + "\\Process(_Total)\\Thread Count", + "\\Process(_Total)\\Handle Count", + "\\System\\System Up Time", + "\\System\\Context Switches/sec", + "\\System\\Processor Queue Length", + "\\Memory\\% Committed Bytes In Use", + "\\Memory\\Available Bytes", + "\\Memory\\Committed Bytes", + "\\Memory\\Cache Bytes", + "\\Memory\\Pool Paged Bytes", + "\\Memory\\Pool Nonpaged Bytes", + "\\Memory\\Pages/sec", + "\\Memory\\Page Faults/sec", + "\\Process(_Total)\\Working Set", + "\\Process(_Total)\\Working Set - Private", + "\\LogicalDisk(_Total)\\% Disk Time", + "\\LogicalDisk(_Total)\\% Disk Read Time", + "\\LogicalDisk(_Total)\\% Disk Write Time", + "\\LogicalDisk(_Total)\\% Idle Time", + "\\LogicalDisk(_Total)\\Disk Bytes/sec", + "\\LogicalDisk(_Total)\\Disk Read Bytes/sec", + "\\LogicalDisk(_Total)\\Disk Write Bytes/sec", + "\\LogicalDisk(_Total)\\Disk Transfers/sec", + "\\LogicalDisk(_Total)\\Disk Reads/sec", + "\\LogicalDisk(_Total)\\Disk Writes/sec", + "\\LogicalDisk(_Total)\\Avg. Disk sec/Transfer", + "\\LogicalDisk(_Total)\\Avg. Disk sec/Read", + "\\LogicalDisk(_Total)\\Avg. Disk sec/Write", + "\\LogicalDisk(_Total)\\Avg. Disk Queue Length", + "\\LogicalDisk(_Total)\\Avg. Disk Read Queue Length", + "\\LogicalDisk(_Total)\\Avg. Disk Write Queue Length", + "\\LogicalDisk(_Total)\\% Free Space", + "\\LogicalDisk(_Total)\\Free Megabytes", + "\\Network Interface(*)\\Bytes Total/sec", + "\\Network Interface(*)\\Bytes Sent/sec", + "\\Network Interface(*)\\Bytes Received/sec", + "\\Network Interface(*)\\Packets/sec", + "\\Network Interface(*)\\Packets Sent/sec", + "\\Network Interface(*)\\Packets Received/sec", + "\\Network Interface(*)\\Packets Outbound Errors", + "\\Network Interface(*)\\Packets Received Errors" + ], + "name": "perfCounterDataSource60", + "samplingFrequencyInSeconds": 60, + "streams": [ + "Microsoft-Perf" + ] + } + ] + }, + "description": "Collect Performance Counters and send to Azure Monitor Logs.", + "destinations": { + "logAnalytics": [ + { + "name": "logAnalytics", + "workspaceResourceId": "[parameters('workspaceId')]" + } + ] } - } + }, + "dependsOn": [ + "[resourceId('Microsoft.Compute/virtualMachines/extensions', parameters('vmName'), 'AADLoginForWindows')]" + ] + }, + { + "type": "Microsoft.Insights/dataCollectionRuleAssociations", + "apiVersion": "2022-06-01", + "scope": "[format('Microsoft.Compute/virtualMachines/{0}', parameters('vmName'))]", + "name": "DCRA-VMSS-WEL-LAW", + "properties": { + "description": "Association of data collection rule. Deleting this association will break the data collection for this virtual machine.", + "dataCollectionRuleId": "[resourceId('Microsoft.Insights/dataCollectionRules', 'DCR-Win-Event-Logs-to-LAW')]" + }, + "dependsOn": [ + "[resourceId('Microsoft.Insights/dataCollectionRules', 'DCR-Win-Event-Logs-to-LAW')]", + "[resourceId('Microsoft.Compute/virtualMachines', parameters('vmName'))]" + ] + }, + { + "type": "Microsoft.Insights/dataCollectionRuleAssociations", + "apiVersion": "2022-06-01", + "scope": "[format('Microsoft.Compute/virtualMachines/{0}', parameters('vmName'))]", + "name": "DCRA-VM-PC-LAW", + "properties": { + "description": "Association of data collection rule. Deleting this association will break the data collection for this virtual machine.", + "dataCollectionRuleId": "[resourceId('Microsoft.Insights/dataCollectionRules', 'DCR-Win-Perf-to-LAW')]" + }, + "dependsOn": [ + "[resourceId('Microsoft.Insights/dataCollectionRules', 'DCR-Win-Perf-to-LAW')]", + "[resourceId('Microsoft.Compute/virtualMachines', parameters('vmName'))]" + ] + }, + { + "condition": "[and(parameters('enableMicrosoftEntraIdAuth'), not(empty(parameters('userObjectId'))))]", + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Compute/virtualMachines/{0}', parameters('vmName'))]", + "name": "[guid(resourceId('Microsoft.Compute/virtualMachines', parameters('vmName')), subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1c0163c0-47e6-4577-8991-ea5c82e286e4'), parameters('userObjectId'))]", + "properties": { + "roleDefinitionId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1c0163c0-47e6-4577-8991-ea5c82e286e4')]", + "principalType": "User", + "principalId": "[parameters('userObjectId')]" + }, + "dependsOn": [ + "[resourceId('Microsoft.Compute/virtualMachines', parameters('vmName'))]" + ] + } + ], + "outputs": { + "name": { + "type": "string", + "value": "[parameters('vmName')]" + }, + "id": { + "type": "string", + "value": "[resourceId('Microsoft.Compute/virtualMachines', parameters('vmName'))]" } } } }, "dependsOn": [ - "aiHub", - "cognitiveServices", - "logAnalyticsWorkspace" + "logAnalyticsWorkspace", + "network", + "storageAccount" ] }, "apim": { @@ -86151,7 +71445,7 @@ "_generator": { "name": "bicep", "version": "0.36.1.42791", - "templateHash": "244112826431292448" + "templateHash": "7173901627480655800" } }, "definitions": { @@ -93295,7 +78589,7 @@ "type": "string", "value": "[reference('cosmosDb').outputs.resourceId.value]" }, - "name": { + "cosmosDBname": { "type": "string", "value": "[reference('cosmosDb').outputs.name.value]" } @@ -103391,11 +88685,11 @@ }, "AZURE_AI_HUB_NAME": { "type": "string", - "value": "[reference('aiHub').outputs.name.value]" + "value": "[reference('cognitiveServices').outputs.aiServicesName.value]" }, "AZURE_AI_PROJECT_NAME": { "type": "string", - "value": "[reference('aiHub').outputs.name.value]" + "value": "[reference('project').outputs.projectName.value]" }, "AZURE_BASTION_NAME": { "type": "string", @@ -103423,7 +88717,7 @@ }, "AZURE_STORAGE_ACCOUNT_NAME": { "type": "string", - "value": "[reference('storageAccount').outputs.name.value]" + "value": "[reference('storageAccount').outputs.storageName.value]" }, "AZURE_API_MANAGEMENT_NAME": { "type": "string", @@ -103447,7 +88741,7 @@ }, "AZURE_COSMOS_ACCOUNT_NAME": { "type": "string", - "value": "[if(parameters('cosmosDbEnabled'), reference('cosmosDb').outputs.name.value, '')]" + "value": "[if(parameters('cosmosDbEnabled'), reference('cosmosDb').outputs.cosmosDBname.value, '')]" } } } \ No newline at end of file From 0c6d30737ac039b2b80c3ac6207ce0d0578e4984 Mon Sep 17 00:00:00 2001 From: Prajwal D C Date: Wed, 18 Jun 2025 21:12:30 +0530 Subject: [PATCH 27/44] docs: Added Note related to AZD issues with 1.17.0 version --- docs/github_code_spaces_steps.md | 2 ++ docs/local_environment_steps.md | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/github_code_spaces_steps.md b/docs/github_code_spaces_steps.md index 154bd44..480422a 100644 --- a/docs/github_code_spaces_steps.md +++ b/docs/github_code_spaces_steps.md @@ -45,6 +45,8 @@ You can run this solution using GitHub Codespaces. The button will open a web-ba 10. Now start the deployment of the infrastructure by typing the command “azd up” + > ⚠️ **Note:** The latest version of the Azure Developer CLI (AZD) is currently limited on prompting for missing parameters. The feature flag parameters in this solution have been temporarily defaulted to `'disabled'` until this limitation is lifted and prompting will resume. + ![image showing the terminal in vs code](../img/provisioning/azd_provision_terminal.png) This step will allow you to choose from the subscriptions you have available, based on the account you logged in with in the login step. Next it will prompt you for the region to deploy the resources into as well as any additional Azure resources to be provisioned and configured. diff --git a/docs/local_environment_steps.md b/docs/local_environment_steps.md index 95840f6..1479c77 100644 --- a/docs/local_environment_steps.md +++ b/docs/local_environment_steps.md @@ -30,13 +30,14 @@ azd env new '' ``` Optionally set environment variables via the following commands: - ```powershell azd env set 'AZURE_VM_ADMIN_PASSWORD' '' ``` # Deploy +> ⚠️ **Note:** The latest version of the Azure Developer CLI (AZD) is currently limited on prompting for missing parameters. The feature flag parameters in this solution have been temporarily defaulted to `'disabled'` until this limitation is lifted and prompting will resume. + To provision the necessary Azure resources and deploy the application, run the azd up command: ```powershell azd up From d797996caa831e36d3507ee16d6a28ad94c62247 Mon Sep 17 00:00:00 2001 From: Mike Swantek Date: Wed, 18 Jun 2025 20:15:37 -0400 Subject: [PATCH 28/44] corrected readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ac99842..cbc72cb 100644 --- a/README.md +++ b/README.md @@ -82,7 +82,7 @@ For additional documentation of the default enabled services of this solution ac QUICK DEPLOY -| [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/microsoft/Deploy-Your-AI-Application-In-Production/tree/feature/1RP) | [![Open in Dev Containers](https://img.shields.io/static/v1?style=for-the-badge&label=Dev%20Containers&message=Open&color=blue&logo=visualstudiocode)](https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/microsoft/Deploy-Your-AI-Application-In-Production) | +| [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/microsoft/Deploy-Your-AI-Application-In-Production) | [![Open in Dev Containers](https://img.shields.io/static/v1?style=for-the-badge&label=Dev%20Containers&message=Open&color=blue&logo=visualstudiocode)](https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/microsoft/Deploy-Your-AI-Application-In-Production) | |---|---| [Steps to deploy with GitHub Codespaces](docs/github_code_spaces_steps.md) From 921ac61b70cb2674c1968df23aacae3196b1359f Mon Sep 17 00:00:00 2001 From: Prajwal D C Date: Thu, 19 Jun 2025 20:39:23 +0530 Subject: [PATCH 29/44] fix: fixed Project name to 12 characters --- infra/main.bicep | 5 +---- infra/main.json | 14 ++------------ 2 files changed, 3 insertions(+), 16 deletions(-) diff --git a/infra/main.bicep b/infra/main.bicep index fe85f29..6ac3687 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -8,9 +8,6 @@ param name string @description('Specifies the location for all the Azure resources. Defaults to the location of the resource group.') param location string -@description('Optional. Specifies the connections to be created for the Azure AI Hub workspace. The connections are used to connect to other Azure resources and services.') -param connections connectionType[] = [] - @description('Optional. Specifies the OpenAI deployments to create.') param aiModelDeployments deploymentsType[] = [] @@ -95,7 +92,7 @@ param networkAcls object ={ } @description('Name of the first project') -param projectName string = '${name}proj' +param projectName string = '${take(name, 8)}proj' var defaultTags = { 'azd-env-name': name diff --git a/infra/main.json b/infra/main.json index 44770cf..74635f7 100644 --- a/infra/main.json +++ b/infra/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.36.1.42791", - "templateHash": "15881347792888664493" + "templateHash": "16756402081633722832" } }, "definitions": { @@ -1638,16 +1638,6 @@ "description": "Specifies the location for all the Azure resources. Defaults to the location of the resource group." } }, - "connections": { - "type": "array", - "items": { - "$ref": "#/definitions/connectionType" - }, - "defaultValue": [], - "metadata": { - "description": "Optional. Specifies the connections to be created for the Azure AI Hub workspace. The connections are used to connect to other Azure resources and services." - } - }, "aiModelDeployments": { "type": "array", "items": { @@ -1826,7 +1816,7 @@ }, "projectName": { "type": "string", - "defaultValue": "[format('{0}proj', parameters('name'))]", + "defaultValue": "[format('{0}proj', take(parameters('name'), 8))]", "metadata": { "description": "Name of the first project" } From 2093adc3fcf7abf001a924e542c03e5bec3e083d Mon Sep 17 00:00:00 2001 From: Mike Swantek Date: Fri, 20 Jun 2025 10:36:21 -0400 Subject: [PATCH 30/44] updates to remove AML calls to import. --- docs/Required_roles_scopes_resources.md | 3 +- infra/main.bicep | 2 +- infra/main.json | 2280 ++++--------------- infra/modules/cognitive-services/main.bicep | 1 - 4 files changed, 384 insertions(+), 1902 deletions(-) diff --git a/docs/Required_roles_scopes_resources.md b/docs/Required_roles_scopes_resources.md index 6da77f5..caa4f31 100644 --- a/docs/Required_roles_scopes_resources.md +++ b/docs/Required_roles_scopes_resources.md @@ -17,8 +17,7 @@ Be sure these resource providers are registered in your Azure subscription. To r |Azure Log Analytics|Microsoft.OperationalInsights|/workspaces|An Azure Log Analytics workspace used to collect diagnostics| |Azure Key Vault|Microsoft.KeyVault|/vaults|An Azure Key Vault instance associated with the Azure AI Foundry Hub| |Azure Storage Account|Microsoft.Storage|/storageAccounts|An Azure Storage instance associated with the Azure AI Foundry Hub| -|Azure Container Registry|Microsoft.ContainerRegistry|/registries|An Azure Container Registry instance associated with the Azure AI Foundry Hub| -|Azure AI Hub / Project|Microsoft.MachineLearningServices|/workspaces|An Azure AI Studio Hub and Project (Azure ML Workspace of kind ‘hub’ and ‘project’)| +|Azure Container Registry|Microsoft.ContainerRegistry|/registries|An Azure Container Registry instance associated with the Azure AI Foundry Account| |Azure AI Services|Microsoft.CognitiveServices|/accounts|An Azure AI Services as the model-as-a-service endpoint provider including GPT-4o and ADA Text Embeddings model deployments| |Azure Virtual Network|Microsoft.Network|/virtualNetworks|A bring-your-own (BYO) virtual network hosting a virtual machine to connect to Azure AI Foundry which will be behind a private endpoint when in network isolation mode. | |Bastion Host|Microsoft.Network||A Bastion Host defined in the BYO virtual network that provides RDP connectivity to the jumpbox virtual machine| diff --git a/infra/main.bicep b/infra/main.bicep index 6ac3687..a3b70f0 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -358,7 +358,7 @@ module sqlServer 'modules/sqlServer.bicep' = if (sqlServerEnabled) { } import { sqlDatabaseType, databasePropertyType, deploymentsType } from 'modules/customTypes.bicep' -import { connectionType } from 'br/public:avm/res/machine-learning-services/workspace:0.10.1' + output AZURE_KEY_VAULT_NAME string = keyvault.outputs.name output AZURE_AI_SERVICES_NAME string = cognitiveServices.outputs.aiServicesName diff --git a/infra/main.json b/infra/main.json index 74635f7..e2b780f 100644 --- a/infra/main.json +++ b/infra/main.json @@ -6,1473 +6,715 @@ "_generator": { "name": "bicep", "version": "0.36.1.42791", - "templateHash": "16756402081633722832" + "templateHash": "14037525013444695971" } }, "definitions": { - "_1._10": { + "_1.diagnosticSettingFullType": { "type": "object", "properties": { - "authType": { + "name": { "type": "string", - "allowedValues": [ - "PAT" - ], + "nullable": true, "metadata": { - "description": "Required. The authentication type of the connection target." + "description": "Optional. The name of the diagnostic setting." } }, - "credentials": { - "$ref": "#/definitions/_1._18", + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, "metadata": { - "description": "Required. The credentials for the connection." + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/machine-learning-services/workspace:0.10.1", - "originalIdentifier": "#/definitions/_2.patAuthTypeWorkspaceConnectionPropertyType" - } - } - }, - "_1._11": { - "type": "object", - "properties": { - "authType": { + }, + "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": [ - "SAS" + "AzureDiagnostics", + "Dedicated" ], + "nullable": true, "metadata": { - "description": "Required. The authentication type of the connection target." + "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." } }, - "credentials": { - "$ref": "#/definitions/_1._20", + "workspaceResourceId": { + "type": "string", + "nullable": true, "metadata": { - "description": "Required. The credentials for the connection." + "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." } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/machine-learning-services/workspace:0.10.1", - "originalIdentifier": "#/definitions/_2.sasAuthTypeWorkspaceConnectionPropertyType" - } - } - }, - "_1._12": { - "type": "object", - "properties": { - "authType": { + }, + "storageAccountResourceId": { "type": "string", - "allowedValues": [ - "ServicePrincipal" - ], + "nullable": true, "metadata": { - "description": "Required. The authentication type of the connection target." + "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." } }, - "credentials": { - "$ref": "#/definitions/_1._19", + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, "metadata": { - "description": "Required. The credentials for the connection." + "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." } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/machine-learning-services/workspace:0.10.1", - "originalIdentifier": "#/definitions/_2.servicePrincipalAuthTypeWorkspaceConnectionPropertyType" - } - } - }, - "_1._13": { - "type": "object", - "properties": { - "authType": { + }, + "eventHubName": { "type": "string", - "allowedValues": [ - "UsernamePassword" - ], + "nullable": true, "metadata": { - "description": "Required. The authentication type of the connection target." + "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." } }, - "credentials": { - "$ref": "#/definitions/_1._21", + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, "metadata": { - "description": "Required. The credentials for the connection." + "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 both logs & metrics are supported by the resource provider.", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/machine-learning-services/workspace:0.10.1", - "originalIdentifier": "#/definitions/_2.usernamePasswordAuthTypeWorkspaceConnectionPropertyType" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, - "_1._14": { + "_2.databaseSkuType": { "type": "object", "properties": { - "accessKeyId": { - "type": "string", + "capacity": { + "type": "int", + "nullable": true, "metadata": { - "description": "Required. The connection access key ID." + "description": "Optional. The capacity of the particular SKU." } }, - "secretAccessKey": { + "family": { "type": "string", + "nullable": true, "metadata": { - "description": "Required. The connection secret access key." + "description": "Optional. If the service has different generations of hardware, for the same SKU, then that can be captured here." } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/machine-learning-services/workspace:0.10.1", - "originalIdentifier": "#/definitions/_2.workspaceConnectionAccessKeyType" - } - } - }, - "_1._15": { - "type": "object", - "properties": { - "key": { + }, + "name": { "type": "string", "metadata": { - "description": "Required. The connection API key." + "description": "Required. The name of the SKU, typically, a letter + Number code, e.g. P3." } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/machine-learning-services/workspace:0.10.1", - "originalIdentifier": "#/definitions/_2.workspaceConnectionApiKeyType" - } - } - }, - "_1._16": { - "type": "object", - "properties": { - "clientId": { + }, + "size": { "type": "string", + "nullable": true, "metadata": { - "description": "Required. The connection managed identity ID." + "description": "Optional. Size of the particular SKU." } }, - "resourceId": { + "tier": { "type": "string", + "nullable": true, "metadata": { - "description": "Required. The connection managed identity resource ID." + "description": "Optional. The tier or edition of the particular SKU, e.g. Basic, Premium." } } }, "metadata": { + "description": "The database SKU.", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/machine-learning-services/workspace:0.10.1", - "originalIdentifier": "#/definitions/_2.workspaceConnectionManagedIdentityType" + "sourceTemplate": "modules/customTypes.bicep" } } }, - "_1._17": { + "_2.longTermBackupRetentionPolicyType": { "type": "object", "properties": { - "authUrl": { + "backupStorageAccessTier": { "type": "string", + "allowedValues": [ + "Archive", + "Hot" + ], "nullable": true, "metadata": { - "description": "Conditional. The connection auth URL. Required by Concur connection category." - } - }, - "clientId": { - "type": "string", - "minLength": 36, - "maxLength": 36, - "metadata": { - "description": "Required. The connection client ID in the format of UUID." - } - }, - "clientSecret": { - "type": "string", - "metadata": { - "description": "Required. The connection client secret." + "description": "Optional. The BackupStorageAccessTier for the LTR backups." } }, - "developerToken": { - "type": "string", + "makeBackupsImmutable": { + "type": "bool", "nullable": true, "metadata": { - "description": "Conditional. The connection developer token. Required by GoogleAdWords connection category." + "description": "Optional. The setting whether to make LTR backups immutable." } }, - "password": { + "monthlyRetention": { "type": "string", "nullable": true, "metadata": { - "description": "Conditional. The connection password. Required by Concur and ServiceNow connection categories where AccessToken grant type is 'Password'." + "description": "Optional. Monthly retention in ISO 8601 duration format." } }, - "refreshToken": { + "weeklyRetention": { "type": "string", "nullable": true, "metadata": { - "description": "Conditional. The connection refresh token. Required by GoogleBigQuery, GoogleAdWords, Hubspot, QuickBooks, Square, Xero and Zoho connection categories." + "description": "Optional. Weekly retention in ISO 8601 duration format." } }, - "tenantId": { - "type": "string", + "weekOfYear": { + "type": "int", "nullable": true, "metadata": { - "description": "Required. The connection tenant ID. Required by QuickBooks and Xero connection categories." + "description": "Optional. Week of year backup to keep for yearly retention." } }, - "username": { + "yearlyRetention": { "type": "string", "nullable": true, "metadata": { - "description": "Conditional. The connection username. Required by Concur and ServiceNow connection categories where AccessToken grant type is 'Password'." + "description": "Optional. Yearly retention in ISO 8601 duration format." } } }, "metadata": { + "description": "The long-term backup retention policy for the database.", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/machine-learning-services/workspace:0.10.1", - "originalIdentifier": "#/definitions/_2.workspaceConnectionOAuth2Type" + "sourceTemplate": "modules/customTypes.bicep" } } }, - "_1._18": { + "_2.shortTermBackupRetentionPolicyType": { "type": "object", "properties": { - "pat": { - "type": "string", + "diffBackupIntervalInHours": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Differential backup interval in hours. For Hyperscale tiers this value will be ignored." + } + }, + "retentionDays": { + "type": "int", + "nullable": true, "metadata": { - "description": "Required. The connection personal access token." + "description": "Optional. Point-in-time retention in days." } } }, "metadata": { + "description": "The short-term backup retention policy for the database.", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/machine-learning-services/workspace:0.10.1", - "originalIdentifier": "#/definitions/_2.workspaceConnectionPersonalAccessTokenType" + "sourceTemplate": "modules/customTypes.bicep" } } }, - "_1._19": { + "databasePropertyType": { "type": "object", "properties": { - "clientId": { + "name": { "type": "string", "metadata": { - "description": "Required. The connection client ID." + "description": "Required. The name of the Elastic Pool." } }, - "clientSecret": { - "type": "string", + "tags": { + "type": "object", + "nullable": true, "metadata": { - "description": "Required. The connection client secret." + "description": "Optional. Tags of the resource." } }, - "tenantId": { - "type": "string", + "sku": { + "$ref": "#/definitions/_2.databaseSkuType", + "nullable": true, "metadata": { - "description": "Required. The connection tenant ID." + "description": "Optional. The database SKU." } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/machine-learning-services/workspace:0.10.1", - "originalIdentifier": "#/definitions/_2.workspaceConnectionServicePrincipalType" - } - } - }, - "_1._2": { - "type": "object", - "properties": { - "authType": { + }, + "autoPauseDelay": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Time in minutes after which database is automatically paused. A value of -1 means that automatic pause is disabled." + } + }, + "availabilityZone": { "type": "string", "allowedValues": [ - "AAD" + "1", + "2", + "3", + "NoPreference" ], + "nullable": true, "metadata": { - "description": "Required. The authentication type of the connection target." + "description": "Optional. Specifies the availability zone the database is pinned to." } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/machine-learning-services/workspace:0.10.1", - "originalIdentifier": "#/definitions/_2.aadAuthTypeWorkspaceConnectionPropertyType" - } - } - }, - "_1._20": { - "type": "object", - "properties": { - "sas": { + }, + "catalogCollation": { "type": "string", + "nullable": true, "metadata": { - "description": "Required. The connection SAS token." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/machine-learning-services/workspace:0.10.1", - "originalIdentifier": "#/definitions/_2.workspaceConnectionSharedAccessSignatureType" - } - } - }, - "_1._21": { - "type": "object", - "properties": { - "password": { - "type": "string", - "metadata": { - "description": "Required. The connection password." + "description": "Optional. Collation of the metadata catalog." } }, - "securityToken": { + "collation": { "type": "string", "nullable": true, "metadata": { - "description": "Conditional. The connection security token. Required by connections like SalesForce for extra security in addition to 'UsernamePassword'." + "description": "Optional. The collation of the database." } }, - "username": { - "type": "string", - "metadata": { - "description": "Required. The connection username." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/machine-learning-services/workspace:0.10.1", - "originalIdentifier": "#/definitions/_2.workspaceConnectionUsernamePasswordType" - } - } - }, - "_1._3": { - "type": "object", - "properties": { - "authType": { + "createMode": { "type": "string", "allowedValues": [ - "AccessKey" + "Copy", + "Default", + "OnlineSecondary", + "PointInTimeRestore", + "Recovery", + "Restore", + "RestoreExternalBackup", + "RestoreExternalBackupSecondary", + "RestoreLongTermRetentionBackup", + "Secondary" ], + "nullable": true, "metadata": { - "description": "Required. The authentication type of the connection target." + "description": "Optional. Specifies the mode of database creation." } }, - "credentials": { - "$ref": "#/definitions/_1._14", - "metadata": { - "description": "Required. The credentials for the connection." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/machine-learning-services/workspace:0.10.1", - "originalIdentifier": "#/definitions/_2.accessKeyAuthTypeWorkspaceConnectionPropertyType" - } - } - }, - "_1._4": { - "type": "object", - "properties": { - "authType": { + "elasticPoolResourceId": { "type": "string", - "allowedValues": [ - "ApiKey" - ], + "nullable": true, "metadata": { - "description": "Required. The authentication type of the connection target." + "description": "Optional. The resource identifier of the elastic pool containing this database." } }, - "credentials": { - "$ref": "#/definitions/_1._15", - "metadata": { - "description": "Required. The credentials for the connection." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/machine-learning-services/workspace:0.10.1", - "originalIdentifier": "#/definitions/_2.apiKeyAuthWorkspaceConnectionPropertyType" - } - } - }, - "_1._5": { - "type": "object", - "properties": { - "keys": { - "type": "object", - "properties": {}, - "additionalProperties": { - "type": "string", - "metadata": { - "description": "Required. Key-value pairs for the custom keys." - } - }, - "metadata": { - "description": "Required. The custom keys for the connection." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/machine-learning-services/workspace:0.10.1", - "originalIdentifier": "#/definitions/_2.customKeysType" - } - } - }, - "_1._6": { - "type": "object", - "properties": { - "authType": { + "encryptionProtector": { "type": "string", - "allowedValues": [ - "CustomKeys" - ], + "nullable": true, "metadata": { - "description": "Required. The authentication type of the connection target." + "description": "Optional. The azure key vault URI of the database if it's configured with per Database Customer Managed Keys." } }, - "credentials": { - "$ref": "#/definitions/_1._5", - "metadata": { - "description": "Required. The credentials for the connection." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/machine-learning-services/workspace:0.10.1", - "originalIdentifier": "#/definitions/_2.customKeysWorkspaceConnectionPropertyType" - } - } - }, - "_1._7": { - "type": "object", - "properties": { - "authType": { - "type": "string", - "allowedValues": [ - "ManagedIdentity" - ], + "encryptionProtectorAutoRotation": { + "type": "bool", + "nullable": true, "metadata": { - "description": "Required. The authentication type of the connection target." + "description": "Optional. The flag to enable or disable auto rotation of database encryption protector AKV key." } }, - "credentials": { - "$ref": "#/definitions/_1._16", - "metadata": { - "description": "Required. The credentials for the connection." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/machine-learning-services/workspace:0.10.1", - "originalIdentifier": "#/definitions/_2.managedIdentityAuthTypeWorkspaceConnectionPropertyType" - } - } - }, - "_1._8": { - "type": "object", - "properties": { - "authType": { + "federatedClientId": { "type": "string", - "allowedValues": [ - "None" - ], + "nullable": true, + "minLength": 36, + "maxLength": 36, "metadata": { - "description": "Required. The authentication type of the connection target." + "description": "Optional. The Client id used for cross tenant per database CMK scenario." } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/machine-learning-services/workspace:0.10.1", - "originalIdentifier": "#/definitions/_2.noneAuthTypeWorkspaceConnectionPropertyType" - } - } - }, - "_1._9": { - "type": "object", - "properties": { - "authType": { + }, + "freeLimitExhaustionBehavior": { "type": "string", "allowedValues": [ - "OAuth2" + "AutoPause", + "BillOverUsage" ], - "metadata": { - "description": "Required. The authentication type of the connection target." - } - }, - "credentials": { - "$ref": "#/definitions/_1._17", - "metadata": { - "description": "Required. The credentials for the connection." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/machine-learning-services/workspace:0.10.1", - "originalIdentifier": "#/definitions/_2.oauth2AuthTypeWorkspaceConnectionPropertyType" - } - } - }, - "_1.categoryType": { - "type": "string", - "allowedValues": [ - "ADLSGen2", - "AIServices", - "AmazonMws", - "AmazonRdsForOracle", - "AmazonRdsForSqlServer", - "AmazonRedshift", - "AmazonS3Compatible", - "ApiKey", - "AzureBlob", - "AzureDataExplorer", - "AzureDatabricksDeltaLake", - "AzureMariaDb", - "AzureMySqlDb", - "AzureOneLake", - "AzureOpenAI", - "AzurePostgresDb", - "AzureSqlDb", - "AzureSqlMi", - "AzureSynapseAnalytics", - "AzureTableStorage", - "BingLLMSearch", - "Cassandra", - "CognitiveSearch", - "CognitiveService", - "Concur", - "ContainerRegistry", - "CosmosDb", - "CosmosDbMongoDbApi", - "Couchbase", - "CustomKeys", - "Db2", - "Drill", - "Dynamics", - "DynamicsAx", - "DynamicsCrm", - "Eloqua", - "FileServer", - "FtpServer", - "GenericContainerRegistry", - "GenericHttp", - "GenericRest", - "Git", - "GoogleAdWords", - "GoogleBigQuery", - "GoogleCloudStorage", - "Greenplum", - "Hbase", - "Hdfs", - "Hive", - "Hubspot", - "Impala", - "Informix", - "Jira", - "Magento", - "MariaDb", - "Marketo", - "MicrosoftAccess", - "MongoDbAtlas", - "MongoDbV2", - "MySql", - "Netezza", - "ODataRest", - "Odbc", - "Office365", - "OpenAI", - "Oracle", - "OracleCloudStorage", - "OracleServiceCloud", - "PayPal", - "Phoenix", - "PostgreSql", - "Presto", - "PythonFeed", - "QuickBooks", - "Redis", - "Responsys", - "S3", - "Salesforce", - "SalesforceMarketingCloud", - "SalesforceServiceCloud", - "SapBw", - "SapCloudForCustomer", - "SapEcc", - "SapHana", - "SapOpenHub", - "SapTable", - "Serp", - "Serverless", - "ServiceNow", - "Sftp", - "SharePointOnlineList", - "Shopify", - "Snowflake", - "Spark", - "SqlServer", - "Square", - "Sybase", - "Teradata", - "Vertica", - "WebTable", - "Xero", - "Zoho" - ], - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/machine-learning-services/workspace:0.10.1" - } - } - }, - "_1.connectionPropertyType": { - "type": "secureObject", - "discriminator": { - "propertyName": "authType", - "mapping": { - "AAD": { - "$ref": "#/definitions/_1._2" - }, - "AccessKey": { - "$ref": "#/definitions/_1._3" - }, - "ApiKey": { - "$ref": "#/definitions/_1._4" - }, - "CustomKeys": { - "$ref": "#/definitions/_1._6" - }, - "ManagedIdentity": { - "$ref": "#/definitions/_1._7" - }, - "None": { - "$ref": "#/definitions/_1._8" - }, - "OAuth2": { - "$ref": "#/definitions/_1._9" - }, - "PAT": { - "$ref": "#/definitions/_1._10" - }, - "SAS": { - "$ref": "#/definitions/_1._11" - }, - "ServicePrincipal": { - "$ref": "#/definitions/_1._12" - }, - "UsernamePassword": { - "$ref": "#/definitions/_1._13" - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/machine-learning-services/workspace:0.10.1" - } - } - }, - "_22.diagnosticSettingFullType": { - "type": "object", - "properties": { - "name": { - "type": "string", "nullable": true, "metadata": { - "description": "Optional. The name of the diagnostic setting." + "description": "Optional. Specifies the behavior when monthly free limits are exhausted for the free database." } }, - "logCategoriesAndGroups": { - "type": "array", - "items": { - "type": "object", - "properties": { - "category": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." - } - }, - "categoryGroup": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." - } - }, - "enabled": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Enable or disable the category explicitly. Default is `true`." - } - } - } - }, + "highAvailabilityReplicaCount": { + "type": "int", "nullable": true, "metadata": { - "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + "description": "Optional. The number of secondary replicas associated with the database that are used to provide high availability. Not applicable to a Hyperscale database within an elastic pool." } }, - "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`." - } - } - } - }, + "isLedgerOn": { + "type": "bool", "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." + "description": "Optional. Whether or not this database is a ledger database, which means all tables in the database are ledger tables." } }, - "logAnalyticsDestinationType": { + "licenseType": { "type": "string", "allowedValues": [ - "AzureDiagnostics", - "Dedicated" + "BasePrice", + "LicenseIncluded" ], "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." + "description": "Optional. The license type to apply for this database." } }, - "workspaceResourceId": { + "longTermRetentionBackupResourceId": { "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." + "description": "Optional. The resource identifier of the long term retention backup associated with create operation of this database." } }, - "storageAccountResourceId": { + "maintenanceConfigurationId": { "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." + "description": "Optional. Maintenance configuration id assigned to the database. This configuration defines the period when the maintenance updates will occur." } }, - "eventHubAuthorizationRuleResourceId": { - "type": "string", + "manualCutover": { + "type": "bool", "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." + "description": "Optional. Whether or not customer controlled manual cutover needs to be done during Update Database operation to Hyperscale tier." } }, - "eventHubName": { - "type": "string", + "maxSizeBytes": { + "type": "int", "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." + "description": "Optional. The max size of the database expressed in bytes." } }, - "marketplacePartnerResourceId": { + "minCapacity": { "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": "Optional. Minimal capacity that database will always have allocated, if not paused." } - } - }, - "metadata": { - "description": "An AVM-aligned type for a diagnostic setting. To be used if both logs & metrics are supported by the resource provider.", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } - } - }, - "_23.databaseSkuType": { - "type": "object", - "properties": { - "capacity": { - "type": "int", + }, + "performCutover": { + "type": "bool", "nullable": true, "metadata": { - "description": "Optional. The capacity of the particular SKU." + "description": "Optional. To trigger customer controlled manual cutover during the wait state while Scaling operation is in progress." } }, - "family": { + "preferredEnclaveType": { "type": "string", + "allowedValues": [ + "Default", + "VBS" + ], "nullable": true, "metadata": { - "description": "Optional. If the service has different generations of hardware, for the same SKU, then that can be captured here." + "description": "Optional. Type of enclave requested on the database." } }, - "name": { + "readScale": { "type": "string", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "nullable": true, "metadata": { - "description": "Required. The name of the SKU, typically, a letter + Number code, e.g. P3." + "description": "Optional. The state of read-only routing. If enabled, connections that have application intent set to readonly in their connection string may be routed to a readonly secondary replica in the same region. Not applicable to a Hyperscale database within an elastic pool." } }, - "size": { + "recoverableDatabaseResourceId": { "type": "string", "nullable": true, "metadata": { - "description": "Optional. Size of the particular SKU." + "description": "Optional. The resource identifier of the recoverable database associated with create operation of this database." } }, - "tier": { + "recoveryServicesRecoveryPointResourceId": { "type": "string", "nullable": true, "metadata": { - "description": "Optional. The tier or edition of the particular SKU, e.g. Basic, Premium." + "description": "Optional. The resource identifier of the recovery point associated with create operation of this database." } - } - }, - "metadata": { - "description": "The database SKU.", - "__bicep_imported_from!": { - "sourceTemplate": "modules/customTypes.bicep" - } - } - }, - "_23.longTermBackupRetentionPolicyType": { - "type": "object", - "properties": { - "backupStorageAccessTier": { + }, + "requestedBackupStorageRedundancy": { "type": "string", "allowedValues": [ - "Archive", - "Hot" + "Geo", + "GeoZone", + "Local", + "Zone" ], "nullable": true, "metadata": { - "description": "Optional. The BackupStorageAccessTier for the LTR backups." + "description": "Optional. The storage account type to be used to store backups for this database." } }, - "makeBackupsImmutable": { - "type": "bool", + "restorableDroppedDatabaseResourceId": { + "type": "string", "nullable": true, "metadata": { - "description": "Optional. The setting whether to make LTR backups immutable." + "description": "Optional. The resource identifier of the restorable dropped database associated with create operation of this database." } }, - "monthlyRetention": { + "restorePointInTime": { "type": "string", "nullable": true, "metadata": { - "description": "Optional. Monthly retention in ISO 8601 duration format." + "description": "Optional. Specifies the point in time (ISO8601 format) of the source database that will be restored to create the new database." } }, - "weeklyRetention": { + "sampleName": { "type": "string", "nullable": true, "metadata": { - "description": "Optional. Weekly retention in ISO 8601 duration format." + "description": "Optional. The name of the sample schema to apply when creating this database." } }, - "weekOfYear": { - "type": "int", + "secondaryType": { + "type": "string", + "allowedValues": [ + "Geo", + "Named", + "Standby" + ], "nullable": true, "metadata": { - "description": "Optional. Week of year backup to keep for yearly retention." + "description": "Optional. The secondary type of the database if it is a secondary." } }, - "yearlyRetention": { + "sourceDatabaseDeletionDate": { "type": "string", "nullable": true, "metadata": { - "description": "Optional. Yearly retention in ISO 8601 duration format." - } - } - }, - "metadata": { - "description": "The long-term backup retention policy for the database.", - "__bicep_imported_from!": { - "sourceTemplate": "modules/customTypes.bicep" - } - } - }, - "_23.shortTermBackupRetentionPolicyType": { - "type": "object", - "properties": { - "diffBackupIntervalInHours": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. Differential backup interval in hours. For Hyperscale tiers this value will be ignored." + "description": "Optional. Specifies the time that the database was deleted." } }, - "retentionDays": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. Point-in-time retention in days." - } - } - }, - "metadata": { - "description": "The short-term backup retention policy for the database.", - "__bicep_imported_from!": { - "sourceTemplate": "modules/customTypes.bicep" - } - } - }, - "connectionType": { - "type": "object", - "properties": { - "name": { + "sourceDatabaseResourceId": { "type": "string", + "nullable": true, "metadata": { - "description": "Required. Name of the connection to create." - } - }, - "category": { - "$ref": "#/definitions/_1.categoryType", - "metadata": { - "description": "Required. Category of the connection." + "description": "Optional. The resource identifier of the source database associated with create operation of this database." } }, - "expiryTime": { + "sourceResourceId": { "type": "string", "nullable": true, "metadata": { - "description": "Optional. The expiry time of the connection." + "description": "Optional. The resource identifier of the source associated with the create operation of this database." } }, - "isSharedToAll": { + "useFreeLimit": { "type": "bool", "nullable": true, "metadata": { - "description": "Optional. Indicates whether the connection is shared to all users in the workspace." + "description": "Optional. Whether or not the database uses free monthly limits. Allowed on one database in a subscription." } }, - "metadata": { - "type": "object", - "properties": {}, - "additionalProperties": { - "type": "string", - "metadata": { - "description": "Required. The metadata key-value pairs." - } - }, + "zoneRedundant": { + "type": "bool", "nullable": true, "metadata": { - "description": "Optional. User metadata for the connection." + "description": "Optional. Whether or not this database is zone redundant, which means the replicas of this database will be spread across multiple availability zones." } }, - "sharedUserList": { + "diagnosticSettings": { "type": "array", "items": { - "type": "string" + "$ref": "#/definitions/_1.diagnosticSettingFullType" }, "nullable": true, "metadata": { - "description": "Optional. The shared user list of the connection." - } - }, - "target": { - "type": "string", - "metadata": { - "description": "Required. The target of the connection." + "description": "Optional. The diagnostic settings of the service." } }, - "value": { - "type": "string", + "backupShortTermRetentionPolicy": { + "$ref": "#/definitions/_2.shortTermBackupRetentionPolicyType", "nullable": true, "metadata": { - "description": "Optional. Value details of the workspace connection." + "description": "Optional. The short term backup retention policy for the database." } }, - "connectionProperties": { - "$ref": "#/definitions/_1.connectionPropertyType", + "backupLongTermRetentionPolicy": { + "$ref": "#/definitions/_2.longTermBackupRetentionPolicyType", + "nullable": true, "metadata": { - "description": "Required. The properties of the connection, specific to the auth type." + "description": "Optional. The long term backup retention policy for the database." } } }, "metadata": { "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/machine-learning-services/workspace:0.10.1" + "sourceTemplate": "modules/customTypes.bicep" } } }, - "databasePropertyType": { + "deploymentsType": { "type": "object", "properties": { "name": { "type": "string", + "nullable": true, "metadata": { - "description": "Required. The name of the Elastic Pool." + "description": "Optional. Specify the name of cognitive service account deployment." } }, - "tags": { + "model": { "type": "object", - "nullable": true, + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of Cognitive Services account deployment model." + } + }, + "format": { + "type": "string", + "metadata": { + "description": "Required. The format of Cognitive Services account deployment model." + } + }, + "version": { + "type": "string", + "metadata": { + "description": "Required. The version of Cognitive Services account deployment model." + } + } + }, "metadata": { - "description": "Optional. Tags of the resource." + "description": "Required. Properties of Cognitive Services account deployment model." } }, "sku": { - "$ref": "#/definitions/_23.databaseSkuType", - "nullable": true, - "metadata": { - "description": "Optional. The database SKU." - } - }, - "autoPauseDelay": { - "type": "int", + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource model definition representing SKU." + } + }, + "capacity": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The capacity of the resource model definition representing SKU." + } + }, + "tier": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The tier of the resource model definition representing SKU." + } + }, + "size": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The size of the resource model definition representing SKU." + } + }, + "family": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The family of the resource model definition representing SKU." + } + } + }, "nullable": true, "metadata": { - "description": "Optional. Time in minutes after which database is automatically paused. A value of -1 means that automatic pause is disabled." + "description": "Optional. The resource model definition representing SKU." } }, - "availabilityZone": { + "raiPolicyName": { "type": "string", - "allowedValues": [ - "1", - "2", - "3", - "NoPreference" - ], "nullable": true, "metadata": { - "description": "Optional. Specifies the availability zone the database is pinned to." + "description": "Optional. The name of RAI policy." } }, - "catalogCollation": { + "versionUpgradeOption": { "type": "string", "nullable": true, "metadata": { - "description": "Optional. Collation of the metadata catalog." + "description": "Optional. The version upgrade option." } - }, - "collation": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The collation of the database." - } - }, - "createMode": { - "type": "string", - "allowedValues": [ - "Copy", - "Default", - "OnlineSecondary", - "PointInTimeRestore", - "Recovery", - "Restore", - "RestoreExternalBackup", - "RestoreExternalBackupSecondary", - "RestoreLongTermRetentionBackup", - "Secondary" - ], - "nullable": true, - "metadata": { - "description": "Optional. Specifies the mode of database creation." - } - }, - "elasticPoolResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The resource identifier of the elastic pool containing this database." - } - }, - "encryptionProtector": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The azure key vault URI of the database if it's configured with per Database Customer Managed Keys." - } - }, - "encryptionProtectorAutoRotation": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. The flag to enable or disable auto rotation of database encryption protector AKV key." - } - }, - "federatedClientId": { - "type": "string", - "nullable": true, - "minLength": 36, - "maxLength": 36, - "metadata": { - "description": "Optional. The Client id used for cross tenant per database CMK scenario." - } - }, - "freeLimitExhaustionBehavior": { + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "modules/customTypes.bicep" + } + } + }, + "sqlDatabaseType": { + "type": "object", + "properties": { + "name": { "type": "string", - "allowedValues": [ - "AutoPause", - "BillOverUsage" - ], - "nullable": true, "metadata": { - "description": "Optional. Specifies the behavior when monthly free limits are exhausted for the free database." + "description": "Required. Name of the SQL database ." } }, - "highAvailabilityReplicaCount": { + "throughput": { "type": "int", "nullable": true, "metadata": { - "description": "Optional. The number of secondary replicas associated with the database that are used to provide high availability. Not applicable to a Hyperscale database within an elastic pool." - } - }, - "isLedgerOn": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Whether or not this database is a ledger database, which means all tables in the database are ledger tables." - } - }, - "licenseType": { - "type": "string", - "allowedValues": [ - "BasePrice", - "LicenseIncluded" - ], - "nullable": true, - "metadata": { - "description": "Optional. The license type to apply for this database." - } - }, - "longTermRetentionBackupResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The resource identifier of the long term retention backup associated with create operation of this database." - } - }, - "maintenanceConfigurationId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Maintenance configuration id assigned to the database. This configuration defines the period when the maintenance updates will occur." - } - }, - "manualCutover": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Whether or not customer controlled manual cutover needs to be done during Update Database operation to Hyperscale tier." + "description": "Optional. Default to 400. Request units per second. Will be ignored if autoscaleSettingsMaxThroughput is used. Setting throughput at the database level is only recommended for development/test or when workload across all containers in the shared throughput database is uniform. For best performance for large production workloads, it is recommended to set dedicated throughput (autoscale or manual) at the container level and not at the database level." } }, - "maxSizeBytes": { + "autoscaleSettingsMaxThroughput": { "type": "int", "nullable": true, "metadata": { - "description": "Optional. The max size of the database expressed in bytes." - } - }, - "minCapacity": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Minimal capacity that database will always have allocated, if not paused." - } - }, - "performCutover": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. To trigger customer controlled manual cutover during the wait state while Scaling operation is in progress." - } - }, - "preferredEnclaveType": { - "type": "string", - "allowedValues": [ - "Default", - "VBS" - ], - "nullable": true, - "metadata": { - "description": "Optional. Type of enclave requested on the database." - } - }, - "readScale": { - "type": "string", - "allowedValues": [ - "Disabled", - "Enabled" - ], - "nullable": true, - "metadata": { - "description": "Optional. The state of read-only routing. If enabled, connections that have application intent set to readonly in their connection string may be routed to a readonly secondary replica in the same region. Not applicable to a Hyperscale database within an elastic pool." - } - }, - "recoverableDatabaseResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The resource identifier of the recoverable database associated with create operation of this database." - } - }, - "recoveryServicesRecoveryPointResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The resource identifier of the recovery point associated with create operation of this database." - } - }, - "requestedBackupStorageRedundancy": { - "type": "string", - "allowedValues": [ - "Geo", - "GeoZone", - "Local", - "Zone" - ], - "nullable": true, - "metadata": { - "description": "Optional. The storage account type to be used to store backups for this database." - } - }, - "restorableDroppedDatabaseResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The resource identifier of the restorable dropped database associated with create operation of this database." - } - }, - "restorePointInTime": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Specifies the point in time (ISO8601 format) of the source database that will be restored to create the new database." - } - }, - "sampleName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the sample schema to apply when creating this database." - } - }, - "secondaryType": { - "type": "string", - "allowedValues": [ - "Geo", - "Named", - "Standby" - ], - "nullable": true, - "metadata": { - "description": "Optional. The secondary type of the database if it is a secondary." - } - }, - "sourceDatabaseDeletionDate": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Specifies the time that the database was deleted." - } - }, - "sourceDatabaseResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The resource identifier of the source database associated with create operation of this database." - } - }, - "sourceResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The resource identifier of the source associated with the create operation of this database." - } - }, - "useFreeLimit": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Whether or not the database uses free monthly limits. Allowed on one database in a subscription." - } - }, - "zoneRedundant": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Whether or not this database is zone redundant, which means the replicas of this database will be spread across multiple availability zones." + "description": "Optional. Specifies the Autoscale settings and represents maximum throughput, the resource can scale up to. The autoscale throughput should have valid throughput values between 1000 and 1000000 inclusive in increments of 1000. If value is set to null, then autoscale will be disabled. Setting throughput at the database level is only recommended for development/test or when workload across all containers in the shared throughput database is uniform. For best performance for large production workloads, it is recommended to set dedicated throughput (autoscale or manual) at the container level and not at the database level." } }, - "diagnosticSettings": { - "type": "array", - "items": { - "$ref": "#/definitions/_22.diagnosticSettingFullType" - }, - "nullable": true, - "metadata": { - "description": "Optional. The diagnostic settings of the service." - } - }, - "backupShortTermRetentionPolicy": { - "$ref": "#/definitions/_23.shortTermBackupRetentionPolicyType", - "nullable": true, - "metadata": { - "description": "Optional. The short term backup retention policy for the database." - } - }, - "backupLongTermRetentionPolicy": { - "$ref": "#/definitions/_23.longTermBackupRetentionPolicyType", - "nullable": true, - "metadata": { - "description": "Optional. The long term backup retention policy for the database." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "modules/customTypes.bicep" - } - } - }, - "deploymentsType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Specify the name of cognitive service account deployment." - } - }, - "model": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of Cognitive Services account deployment model." - } - }, - "format": { - "type": "string", - "metadata": { - "description": "Required. The format of Cognitive Services account deployment model." - } - }, - "version": { - "type": "string", - "metadata": { - "description": "Required. The version of Cognitive Services account deployment model." - } - } - }, - "metadata": { - "description": "Required. Properties of Cognitive Services account deployment model." - } - }, - "sku": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the resource model definition representing SKU." - } - }, - "capacity": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The capacity of the resource model definition representing SKU." - } - }, - "tier": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The tier of the resource model definition representing SKU." - } - }, - "size": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The size of the resource model definition representing SKU." - } - }, - "family": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The family of the resource model definition representing SKU." - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. The resource model definition representing SKU." - } - }, - "raiPolicyName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of RAI policy." - } - }, - "versionUpgradeOption": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The version upgrade option." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "modules/customTypes.bicep" - } - } - }, - "sqlDatabaseType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. Name of the SQL database ." - } - }, - "throughput": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. Default to 400. Request units per second. Will be ignored if autoscaleSettingsMaxThroughput is used. Setting throughput at the database level is only recommended for development/test or when workload across all containers in the shared throughput database is uniform. For best performance for large production workloads, it is recommended to set dedicated throughput (autoscale or manual) at the container level and not at the database level." - } - }, - "autoscaleSettingsMaxThroughput": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. Specifies the Autoscale settings and represents maximum throughput, the resource can scale up to. The autoscale throughput should have valid throughput values between 1000 and 1000000 inclusive in increments of 1000. If value is set to null, then autoscale will be disabled. Setting throughput at the database level is only recommended for development/test or when workload across all containers in the shared throughput database is uniform. For best performance for large production workloads, it is recommended to set dedicated throughput (autoscale or manual) at the container level and not at the database level." - } - }, - "containers": { + "containers": { "type": "array", "items": { "type": "object", @@ -30980,768 +30222,10 @@ "_generator": { "name": "bicep", "version": "0.36.1.42791", - "templateHash": "13701972717161400247" + "templateHash": "532244891627057375" } }, "definitions": { - "_1._10": { - "type": "object", - "properties": { - "authType": { - "type": "string", - "allowedValues": [ - "PAT" - ], - "metadata": { - "description": "Required. The authentication type of the connection target." - } - }, - "credentials": { - "$ref": "#/definitions/_1._18", - "metadata": { - "description": "Required. The credentials for the connection." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/machine-learning-services/workspace:0.10.1", - "originalIdentifier": "#/definitions/_2.patAuthTypeWorkspaceConnectionPropertyType" - } - } - }, - "_1._11": { - "type": "object", - "properties": { - "authType": { - "type": "string", - "allowedValues": [ - "SAS" - ], - "metadata": { - "description": "Required. The authentication type of the connection target." - } - }, - "credentials": { - "$ref": "#/definitions/_1._20", - "metadata": { - "description": "Required. The credentials for the connection." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/machine-learning-services/workspace:0.10.1", - "originalIdentifier": "#/definitions/_2.sasAuthTypeWorkspaceConnectionPropertyType" - } - } - }, - "_1._12": { - "type": "object", - "properties": { - "authType": { - "type": "string", - "allowedValues": [ - "ServicePrincipal" - ], - "metadata": { - "description": "Required. The authentication type of the connection target." - } - }, - "credentials": { - "$ref": "#/definitions/_1._19", - "metadata": { - "description": "Required. The credentials for the connection." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/machine-learning-services/workspace:0.10.1", - "originalIdentifier": "#/definitions/_2.servicePrincipalAuthTypeWorkspaceConnectionPropertyType" - } - } - }, - "_1._13": { - "type": "object", - "properties": { - "authType": { - "type": "string", - "allowedValues": [ - "UsernamePassword" - ], - "metadata": { - "description": "Required. The authentication type of the connection target." - } - }, - "credentials": { - "$ref": "#/definitions/_1._21", - "metadata": { - "description": "Required. The credentials for the connection." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/machine-learning-services/workspace:0.10.1", - "originalIdentifier": "#/definitions/_2.usernamePasswordAuthTypeWorkspaceConnectionPropertyType" - } - } - }, - "_1._14": { - "type": "object", - "properties": { - "accessKeyId": { - "type": "string", - "metadata": { - "description": "Required. The connection access key ID." - } - }, - "secretAccessKey": { - "type": "string", - "metadata": { - "description": "Required. The connection secret access key." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/machine-learning-services/workspace:0.10.1", - "originalIdentifier": "#/definitions/_2.workspaceConnectionAccessKeyType" - } - } - }, - "_1._15": { - "type": "object", - "properties": { - "key": { - "type": "string", - "metadata": { - "description": "Required. The connection API key." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/machine-learning-services/workspace:0.10.1", - "originalIdentifier": "#/definitions/_2.workspaceConnectionApiKeyType" - } - } - }, - "_1._16": { - "type": "object", - "properties": { - "clientId": { - "type": "string", - "metadata": { - "description": "Required. The connection managed identity ID." - } - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "Required. The connection managed identity resource ID." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/machine-learning-services/workspace:0.10.1", - "originalIdentifier": "#/definitions/_2.workspaceConnectionManagedIdentityType" - } - } - }, - "_1._17": { - "type": "object", - "properties": { - "authUrl": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Conditional. The connection auth URL. Required by Concur connection category." - } - }, - "clientId": { - "type": "string", - "minLength": 36, - "maxLength": 36, - "metadata": { - "description": "Required. The connection client ID in the format of UUID." - } - }, - "clientSecret": { - "type": "string", - "metadata": { - "description": "Required. The connection client secret." - } - }, - "developerToken": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Conditional. The connection developer token. Required by GoogleAdWords connection category." - } - }, - "password": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Conditional. The connection password. Required by Concur and ServiceNow connection categories where AccessToken grant type is 'Password'." - } - }, - "refreshToken": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Conditional. The connection refresh token. Required by GoogleBigQuery, GoogleAdWords, Hubspot, QuickBooks, Square, Xero and Zoho connection categories." - } - }, - "tenantId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Required. The connection tenant ID. Required by QuickBooks and Xero connection categories." - } - }, - "username": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Conditional. The connection username. Required by Concur and ServiceNow connection categories where AccessToken grant type is 'Password'." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/machine-learning-services/workspace:0.10.1", - "originalIdentifier": "#/definitions/_2.workspaceConnectionOAuth2Type" - } - } - }, - "_1._18": { - "type": "object", - "properties": { - "pat": { - "type": "string", - "metadata": { - "description": "Required. The connection personal access token." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/machine-learning-services/workspace:0.10.1", - "originalIdentifier": "#/definitions/_2.workspaceConnectionPersonalAccessTokenType" - } - } - }, - "_1._19": { - "type": "object", - "properties": { - "clientId": { - "type": "string", - "metadata": { - "description": "Required. The connection client ID." - } - }, - "clientSecret": { - "type": "string", - "metadata": { - "description": "Required. The connection client secret." - } - }, - "tenantId": { - "type": "string", - "metadata": { - "description": "Required. The connection tenant ID." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/machine-learning-services/workspace:0.10.1", - "originalIdentifier": "#/definitions/_2.workspaceConnectionServicePrincipalType" - } - } - }, - "_1._2": { - "type": "object", - "properties": { - "authType": { - "type": "string", - "allowedValues": [ - "AAD" - ], - "metadata": { - "description": "Required. The authentication type of the connection target." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/machine-learning-services/workspace:0.10.1", - "originalIdentifier": "#/definitions/_2.aadAuthTypeWorkspaceConnectionPropertyType" - } - } - }, - "_1._20": { - "type": "object", - "properties": { - "sas": { - "type": "string", - "metadata": { - "description": "Required. The connection SAS token." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/machine-learning-services/workspace:0.10.1", - "originalIdentifier": "#/definitions/_2.workspaceConnectionSharedAccessSignatureType" - } - } - }, - "_1._21": { - "type": "object", - "properties": { - "password": { - "type": "string", - "metadata": { - "description": "Required. The connection password." - } - }, - "securityToken": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Conditional. The connection security token. Required by connections like SalesForce for extra security in addition to 'UsernamePassword'." - } - }, - "username": { - "type": "string", - "metadata": { - "description": "Required. The connection username." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/machine-learning-services/workspace:0.10.1", - "originalIdentifier": "#/definitions/_2.workspaceConnectionUsernamePasswordType" - } - } - }, - "_1._3": { - "type": "object", - "properties": { - "authType": { - "type": "string", - "allowedValues": [ - "AccessKey" - ], - "metadata": { - "description": "Required. The authentication type of the connection target." - } - }, - "credentials": { - "$ref": "#/definitions/_1._14", - "metadata": { - "description": "Required. The credentials for the connection." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/machine-learning-services/workspace:0.10.1", - "originalIdentifier": "#/definitions/_2.accessKeyAuthTypeWorkspaceConnectionPropertyType" - } - } - }, - "_1._4": { - "type": "object", - "properties": { - "authType": { - "type": "string", - "allowedValues": [ - "ApiKey" - ], - "metadata": { - "description": "Required. The authentication type of the connection target." - } - }, - "credentials": { - "$ref": "#/definitions/_1._15", - "metadata": { - "description": "Required. The credentials for the connection." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/machine-learning-services/workspace:0.10.1", - "originalIdentifier": "#/definitions/_2.apiKeyAuthWorkspaceConnectionPropertyType" - } - } - }, - "_1._5": { - "type": "object", - "properties": { - "keys": { - "type": "object", - "properties": {}, - "additionalProperties": { - "type": "string", - "metadata": { - "description": "Required. Key-value pairs for the custom keys." - } - }, - "metadata": { - "description": "Required. The custom keys for the connection." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/machine-learning-services/workspace:0.10.1", - "originalIdentifier": "#/definitions/_2.customKeysType" - } - } - }, - "_1._6": { - "type": "object", - "properties": { - "authType": { - "type": "string", - "allowedValues": [ - "CustomKeys" - ], - "metadata": { - "description": "Required. The authentication type of the connection target." - } - }, - "credentials": { - "$ref": "#/definitions/_1._5", - "metadata": { - "description": "Required. The credentials for the connection." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/machine-learning-services/workspace:0.10.1", - "originalIdentifier": "#/definitions/_2.customKeysWorkspaceConnectionPropertyType" - } - } - }, - "_1._7": { - "type": "object", - "properties": { - "authType": { - "type": "string", - "allowedValues": [ - "ManagedIdentity" - ], - "metadata": { - "description": "Required. The authentication type of the connection target." - } - }, - "credentials": { - "$ref": "#/definitions/_1._16", - "metadata": { - "description": "Required. The credentials for the connection." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/machine-learning-services/workspace:0.10.1", - "originalIdentifier": "#/definitions/_2.managedIdentityAuthTypeWorkspaceConnectionPropertyType" - } - } - }, - "_1._8": { - "type": "object", - "properties": { - "authType": { - "type": "string", - "allowedValues": [ - "None" - ], - "metadata": { - "description": "Required. The authentication type of the connection target." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/machine-learning-services/workspace:0.10.1", - "originalIdentifier": "#/definitions/_2.noneAuthTypeWorkspaceConnectionPropertyType" - } - } - }, - "_1._9": { - "type": "object", - "properties": { - "authType": { - "type": "string", - "allowedValues": [ - "OAuth2" - ], - "metadata": { - "description": "Required. The authentication type of the connection target." - } - }, - "credentials": { - "$ref": "#/definitions/_1._17", - "metadata": { - "description": "Required. The credentials for the connection." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/machine-learning-services/workspace:0.10.1", - "originalIdentifier": "#/definitions/_2.oauth2AuthTypeWorkspaceConnectionPropertyType" - } - } - }, - "_1.categoryType": { - "type": "string", - "allowedValues": [ - "ADLSGen2", - "AIServices", - "AmazonMws", - "AmazonRdsForOracle", - "AmazonRdsForSqlServer", - "AmazonRedshift", - "AmazonS3Compatible", - "ApiKey", - "AzureBlob", - "AzureDataExplorer", - "AzureDatabricksDeltaLake", - "AzureMariaDb", - "AzureMySqlDb", - "AzureOneLake", - "AzureOpenAI", - "AzurePostgresDb", - "AzureSqlDb", - "AzureSqlMi", - "AzureSynapseAnalytics", - "AzureTableStorage", - "BingLLMSearch", - "Cassandra", - "CognitiveSearch", - "CognitiveService", - "Concur", - "ContainerRegistry", - "CosmosDb", - "CosmosDbMongoDbApi", - "Couchbase", - "CustomKeys", - "Db2", - "Drill", - "Dynamics", - "DynamicsAx", - "DynamicsCrm", - "Eloqua", - "FileServer", - "FtpServer", - "GenericContainerRegistry", - "GenericHttp", - "GenericRest", - "Git", - "GoogleAdWords", - "GoogleBigQuery", - "GoogleCloudStorage", - "Greenplum", - "Hbase", - "Hdfs", - "Hive", - "Hubspot", - "Impala", - "Informix", - "Jira", - "Magento", - "MariaDb", - "Marketo", - "MicrosoftAccess", - "MongoDbAtlas", - "MongoDbV2", - "MySql", - "Netezza", - "ODataRest", - "Odbc", - "Office365", - "OpenAI", - "Oracle", - "OracleCloudStorage", - "OracleServiceCloud", - "PayPal", - "Phoenix", - "PostgreSql", - "Presto", - "PythonFeed", - "QuickBooks", - "Redis", - "Responsys", - "S3", - "Salesforce", - "SalesforceMarketingCloud", - "SalesforceServiceCloud", - "SapBw", - "SapCloudForCustomer", - "SapEcc", - "SapHana", - "SapOpenHub", - "SapTable", - "Serp", - "Serverless", - "ServiceNow", - "Sftp", - "SharePointOnlineList", - "Shopify", - "Snowflake", - "Spark", - "SqlServer", - "Square", - "Sybase", - "Teradata", - "Vertica", - "WebTable", - "Xero", - "Zoho" - ], - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/machine-learning-services/workspace:0.10.1" - } - } - }, - "_1.connectionPropertyType": { - "type": "secureObject", - "discriminator": { - "propertyName": "authType", - "mapping": { - "AAD": { - "$ref": "#/definitions/_1._2" - }, - "AccessKey": { - "$ref": "#/definitions/_1._3" - }, - "ApiKey": { - "$ref": "#/definitions/_1._4" - }, - "CustomKeys": { - "$ref": "#/definitions/_1._6" - }, - "ManagedIdentity": { - "$ref": "#/definitions/_1._7" - }, - "None": { - "$ref": "#/definitions/_1._8" - }, - "OAuth2": { - "$ref": "#/definitions/_1._9" - }, - "PAT": { - "$ref": "#/definitions/_1._10" - }, - "SAS": { - "$ref": "#/definitions/_1._11" - }, - "ServicePrincipal": { - "$ref": "#/definitions/_1._12" - }, - "UsernamePassword": { - "$ref": "#/definitions/_1._13" - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/machine-learning-services/workspace:0.10.1" - } - } - }, - "connectionType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. Name of the connection to create." - } - }, - "category": { - "$ref": "#/definitions/_1.categoryType", - "metadata": { - "description": "Required. Category of the connection." - } - }, - "expiryTime": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The expiry time of the connection." - } - }, - "isSharedToAll": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Indicates whether the connection is shared to all users in the workspace." - } - }, - "metadata": { - "type": "object", - "properties": {}, - "additionalProperties": { - "type": "string", - "metadata": { - "description": "Required. The metadata key-value pairs." - } - }, - "nullable": true, - "metadata": { - "description": "Optional. User metadata for the connection." - } - }, - "sharedUserList": { - "type": "array", - "items": { - "type": "string" - }, - "nullable": true, - "metadata": { - "description": "Optional. The shared user list of the connection." - } - }, - "target": { - "type": "string", - "metadata": { - "description": "Required. The target of the connection." - } - }, - "value": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Value details of the workspace connection." - } - }, - "connectionProperties": { - "$ref": "#/definitions/_1.connectionPropertyType", - "metadata": { - "description": "Required. The properties of the connection, specific to the auth type." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/machine-learning-services/workspace:0.10.1" - } - } - }, "deploymentsType": { "type": "object", "properties": { diff --git a/infra/modules/cognitive-services/main.bicep b/infra/modules/cognitive-services/main.bicep index 16ee02a..5095191 100644 --- a/infra/modules/cognitive-services/main.bicep +++ b/infra/modules/cognitive-services/main.bicep @@ -228,7 +228,6 @@ module documentIntelligence 'service.bicep' = if (documentIntelligenceEnabled) { } import { deploymentsType } from '../customTypes.bicep' -import { connectionType } from 'br/public:avm/res/machine-learning-services/workspace:0.10.1' output aiServicesResourceId string = aiServices.outputs.resourceId output aiServicesName string = aiServices.outputs.name From 5b7f3d8e6de0288a63801198c62bd39cf1c01f99 Mon Sep 17 00:00:00 2001 From: Seth Date: Tue, 1 Jul 2025 15:03:45 -0400 Subject: [PATCH 31/44] Quota - implemented AZD quota check, removed model array for embedding and gpt only, removed scripts --- azure.yaml | 11 --- infra/main.bicep | 47 +++++++--- infra/main.parameters.json | 40 +++------ infra/modules/customTypes.bicep | 16 ++++ scripts/validate_model_deployment_quotas.ps1 | 69 --------------- scripts/validate_model_deployment_quotas.sh | 83 ----------------- scripts/validate_model_quota.ps1 | 66 -------------- scripts/validate_model_quota.sh | 93 -------------------- 8 files changed, 64 insertions(+), 361 deletions(-) delete mode 100644 scripts/validate_model_deployment_quotas.ps1 delete mode 100755 scripts/validate_model_deployment_quotas.sh delete mode 100644 scripts/validate_model_quota.ps1 delete mode 100644 scripts/validate_model_quota.sh diff --git a/azure.yaml b/azure.yaml index c062782..d733b84 100644 --- a/azure.yaml +++ b/azure.yaml @@ -14,15 +14,4 @@ hooks: shell: sh run: chmod u+r+x ./scripts/set_conns_env_vars.sh; ./scripts/set_conns_env_vars.sh interactive: true - continueOnError: false - preprovision: - posix: - shell: sh - run: chmod u+r+x ./scripts/validate_model_deployment_quotas.sh; chmod u+r+x ./scripts/validate_model_quota.sh; ./scripts/validate_model_deployment_quotas.sh --subscription $AZURE_SUBSCRIPTION_ID --location $AZURE_LOCATION --models-parameter "aiModelDeployments" - interactive: false - continueOnError: false - windows: - shell: pwsh - run: ./scripts/validate_model_deployment_quotas.ps1 -Subscription $env:AZURE_SUBSCRIPTION_ID -Location $env:AZURE_LOCATION -ModelsParameter "aiModelDeployments" - interactive: false continueOnError: false \ No newline at end of file diff --git a/infra/main.bicep b/infra/main.bicep index a3b70f0..62cb4ab 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -5,11 +5,27 @@ targetScope = 'resourceGroup' @description('The name of the environment/application. Use alphanumeric characters only.') param name string -@description('Specifies the location for all the Azure resources. Defaults to the location of the resource group.') +@metadata({ azd: { type: 'location' } }) +@description('Specifies the location for all the Azure resources.') param location string -@description('Optional. Specifies the OpenAI deployments to create.') -param aiModelDeployments deploymentsType[] = [] +@description('Specifies the AI embedding model to use for the AI Foundry deployment. This is the model used for text embeddings in AI Foundry. NOTE: Any adjustments to this parameter\'s values must also be made on the aiDeploymentsLocation metadata in the main.bicep file.') +param aiEmbeddingModelDeployment modelDeploymentType + +@description('Specifies the AI chat model to use for the AI Foundry deployment. This is the model used for chat interactions in AI Foundry. NOTE: Any adjustments to this parameter\'s values must also be made on the aiDeploymentsLocation metadata in the main.bicep file.') +param aiGPTModelDeployment modelDeploymentType + +@metadata({ + azd: { + type: 'location' + usageName: [ + 'OpenAI.GlobalStandard.gpt-4o,150' + 'OpenAI.GlobalStandard.text-embedding-3-small,100' + ] + } +}) +@description('Required. Location for AI Foundry deployment. This is the location where the AI Foundry resources will be deployed.') +param aiDeploymentsLocation string @description('Specifies whether creating an Azure Container Registry.') param acrEnabled bool = false @@ -83,12 +99,6 @@ param documentIntelligenceEnabled bool = false param networkAcls object ={ defaultAction: 'Deny' bypass: 'AzureServices' // ✅ Allows trusted Microsoft services - // virtualNetworkRules: [ - // { - // id: networkIsolation ? network.outputs.vmSubnetName : '' - // ignoreMissingVnetServiceEndpoint: true - // } - // ] } @description('Name of the first project') @@ -216,13 +226,26 @@ module cognitiveServices 'modules/cognitive-services/main.bicep' = { params: { name: name resourceToken: resourceToken - location: location + location: aiDeploymentsLocation networkIsolation: networkIsolation networkAcls: networkAcls virtualNetworkResourceId: networkIsolation ? network.outputs.virtualNetworkId : '' virtualNetworkSubnetResourceId: networkIsolation ? network.outputs.vmSubnetId : '' logAnalyticsWorkspaceResourceId: logAnalyticsWorkspace.outputs.resourceId - aiModelDeployments: aiModelDeployments + aiModelDeployments: [ + for model in [aiEmbeddingModelDeployment, aiGPTModelDeployment]: { + name: empty(model.?name) ? model.modelName : model.?name + model: { + name: model.modelName + format: 'OpenAI' + version: model.version + } + sku: { + name: 'GlobalStandard' + capacity: model.capacity + } + } + ] userObjectId: userObjectId contentSafetyEnabled: contentSafetyEnabled visionEnabled: visionEnabled @@ -357,7 +380,7 @@ module sqlServer 'modules/sqlServer.bicep' = if (sqlServerEnabled) { } } -import { sqlDatabaseType, databasePropertyType, deploymentsType } from 'modules/customTypes.bicep' +import { sqlDatabaseType, databasePropertyType, modelDeploymentType } from 'modules/customTypes.bicep' output AZURE_KEY_VAULT_NAME string = keyvault.outputs.name diff --git a/infra/main.parameters.json b/infra/main.parameters.json index 9369f6e..7f5556c 100644 --- a/infra/main.parameters.json +++ b/infra/main.parameters.json @@ -50,33 +50,19 @@ "bingGroundingEnabled": { "value": "${AZURE_BING_GROUNDING_ENABLED}" }, - "aiModelDeployments": { - "value": [ - { - "name": "textembed", - "model": { - "name": "text-embedding-ada-002", - "format": "OpenAI", - "version": "2" - }, - "sku": { - "name": "Standard", - "capacity": 10 - } - }, - { - "name": "gpt", - "model": { - "name": "gpt-4o", - "version": "2024-05-13", - "format": "OpenAI" - }, - "sku": { - "name": "GlobalStandard", - "capacity": 10 - } - } - ] + "aiEmbeddingModelDeployment": { + "value": { + "modelName": "text-embedding-3-small", + "version": "1", + "capacity": 100 + } + }, + "aiGPTModelDeployment": { + "value": { + "modelName": "gpt-4o", + "version": "2024-05-13", + "capacity": 150 + } } } } \ No newline at end of file diff --git a/infra/modules/customTypes.bicep b/infra/modules/customTypes.bicep index 8779ca9..6fe8473 100644 --- a/infra/modules/customTypes.bicep +++ b/infra/modules/customTypes.bicep @@ -253,6 +253,22 @@ type longTermBackupRetentionPolicyType = { yearlyRetention: string? } +@export() +@description('The AI model deployment type for Cognitive Services account.') +type modelDeploymentType = { + @description('Optional. The name of the Cognitive Services account deployment model. The modelName will be used by default if not specified.') + name: string? + + @description('Required. The format of the Cognitive Services account deployment model.') + modelName: string + + @description('Required. The version of the Cognitive Services account deployment model.') + version: string + + @description('Required. The capacity of the resource model definition representing SKU.') + capacity: int +} + @export() type deploymentsType = { @description('Optional. Specify the name of cognitive service account deployment.') diff --git a/scripts/validate_model_deployment_quotas.ps1 b/scripts/validate_model_deployment_quotas.ps1 deleted file mode 100644 index 43e38c2..0000000 --- a/scripts/validate_model_deployment_quotas.ps1 +++ /dev/null @@ -1,69 +0,0 @@ -param ( - [string]$SubscriptionId, - [string]$Location, - [string]$ModelsParameter -) - -# Verify all required parameters are provided -$MissingParams = @() - -if (-not $SubscriptionId) { - $MissingParams += "subscription" -} - -if (-not $Location) { - $MissingParams += "location" -} - -if (-not $ModelsParameter) { - $MissingParams += "models-parameter" -} - -if ($MissingParams.Count -gt 0) { - Write-Error "❌ ERROR: Missing required parameters: $($MissingParams -join ', ')" - Write-Host "Usage: .\validate_model_deployment_quotas.ps1 -SubscriptionId -Location -ModelsParameter " - exit 1 -} - -$JsonContent = Get-Content -Path "./infra/main.parameters.json" -Raw | ConvertFrom-Json - -if (-not $JsonContent) { - Write-Error "❌ ERROR: Failed to parse main.parameters.json. Ensure the JSON file is valid." - exit 1 -} - -$aiModelDeployments = $JsonContent.parameters.$ModelsParameter.value - -if (-not $aiModelDeployments -or -not ($aiModelDeployments -is [System.Collections.IEnumerable])) { - Write-Error "❌ ERROR: The specified property $ModelsParameter does not exist or is not an array." - exit 1 -} - -az account set --subscription $SubscriptionId -Write-Host "🎯 Active Subscription: $(az account show --query '[name, id]' --output tsv)" - -$QuotaAvailable = $true - -foreach ($deployment in $aiModelDeployments) { - $name = $deployment.name - $model = $deployment.model.name - $type = $deployment.sku.name - $capacity = $deployment.sku.capacity - - Write-Host "🔍 Validating model deployment: $name ..." - & .\scripts\validate_model_quota.ps1 -Location $Location -Model $model -Capacity $capacity -DeploymentType $type - - # Check if the script failed - if ($LASTEXITCODE -ne 0) { - Write-Error "❌ ERROR: Quota validation failed for model deployment: $name" - $QuotaAvailable = $false - } -} - -if (-not $QuotaAvailable) { - Write-Error "❌ ERROR: One or more model deployments failed validation." - exit 1 -} else { - Write-Host "✅ All model deployments passed quota validation successfully." - exit 0 -} \ No newline at end of file diff --git a/scripts/validate_model_deployment_quotas.sh b/scripts/validate_model_deployment_quotas.sh deleted file mode 100755 index 17a5347..0000000 --- a/scripts/validate_model_deployment_quotas.sh +++ /dev/null @@ -1,83 +0,0 @@ -#!/bin/bash - -SUBSCRIPTION_ID="" -LOCATION="" -MODELS_PARAMETER="" - -while [[ $# -gt 0 ]]; do - case "$1" in - --subscription) - SUBSCRIPTION_ID="$2" - shift 2 - ;; - --location) - LOCATION="$2" - shift 2 - ;; - --models-parameter) - MODELS_PARAMETER="$2" - shift 2 - ;; - *) - echo "Unknown option: $1" - exit 1 - ;; - esac -done - -# Verify all required parameters are provided and echo missing ones -MISSING_PARAMS=() - -if [[ -z "$SUBSCRIPTION_ID" ]]; then - MISSING_PARAMS+=("subscription") -fi - -if [[ -z "$LOCATION" ]]; then - MISSING_PARAMS+=("location") -fi - -if [[ -z "$MODELS_PARAMETER" ]]; then - MISSING_PARAMS+=("models-parameter") -fi - -if [[ ${#MISSING_PARAMS[@]} -ne 0 ]]; then - echo "❌ ERROR: Missing required parameters: ${MISSING_PARAMS[*]}" - echo "Usage: $0 --subscription --location --models-parameter " - exit 1 -fi - -aiModelDeployments=$(jq -c ".parameters.$MODELS_PARAMETER.value[]" ./infra/main.parameters.json) - -if [ $? -ne 0 ]; then - echo "Error: Failed to parse main.parameters.json. Ensure jq is installed and the JSON file is valid." - exit 1 -fi - -az account set --subscription "$SUBSCRIPTION_ID" -echo "🎯 Active Subscription: $(az account show --query '[name, id]' --output tsv)" - -quotaAvailable=true - -while IFS= read -r deployment; do - name=$(echo "$deployment" | jq -r '.name') - model=$(echo "$deployment" | jq -r '.model.name') - type=$(echo "$deployment" | jq -r '.sku.name') - capacity=$(echo "$deployment" | jq -r '.sku.capacity') - - echo "🔍 Validating model deployment: $name ..." - ./scripts/validate_model_quota.sh --location "$LOCATION" --model "$model" --capacity $capacity --deployment-type $type - - # Check if the script failed - if [ $? -ne 0 ]; then - echo "❌ ERROR: Quota validation failed for model deployment: $name" - quotaAvailable=false - fi -done <<< "$(echo "$aiModelDeployments")" - -if [ "$quotaAvailable" = false ]; then - echo "❌ ERROR: One or more model deployments failed validation." - exit 1 -else - echo "✅ All model deployments passed quota validation successfully." - exit 0 -fi diff --git a/scripts/validate_model_quota.ps1 b/scripts/validate_model_quota.ps1 deleted file mode 100644 index eda43ae..0000000 --- a/scripts/validate_model_quota.ps1 +++ /dev/null @@ -1,66 +0,0 @@ -param ( - [string]$Location, - [string]$Model, - [string]$DeploymentType = "Standard", - [int]$Capacity -) - -# Verify all required parameters are provided -$MissingParams = @() - -if (-not $Location) { - $MissingParams += "location" -} - -if (-not $Model) { - $MissingParams += "model" -} - -if (-not $Capacity) { - $MissingParams += "capacity" -} - -if (-not $DeploymentType) { - $MissingParams += "deployment-type" -} - -if ($MissingParams.Count -gt 0) { - Write-Error "❌ ERROR: Missing required parameters: $($MissingParams -join ', ')" - Write-Host "Usage: .\validate_model_quota.ps1 -Location -Model -Capacity [-DeploymentType ]" - exit 1 -} - -if ($DeploymentType -ne "Standard" -and $DeploymentType -ne "GlobalStandard") { - Write-Error "❌ ERROR: Invalid deployment type: $DeploymentType. Allowed values are 'Standard' or 'GlobalStandard'." - exit 1 -} - -$ModelType = "OpenAI.$DeploymentType.$Model" - -Write-Host "🔍 Checking quota for $ModelType in $Location ..." - -# Get model quota information -$ModelInfo = az cognitiveservices usage list --location $Location --query "[?name.value=='$ModelType']" --output json | ConvertFrom-Json - -if (-not $ModelInfo) { - Write-Error "❌ ERROR: No quota information found for model: $Model in location: $Location for model type: $ModelType." - exit 1 -} - -if ($ModelInfo) { - $CurrentValue = ($ModelInfo | Where-Object { $_.name.value -eq $ModelType }).currentValue - $Limit = ($ModelInfo | Where-Object { $_.name.value -eq $ModelType }).limit - - $CurrentValue = [int]($CurrentValue -replace '\.0+$', '') # Remove decimals - $Limit = [int]($Limit -replace '\.0+$', '') # Remove decimals - - $Available = $Limit - $CurrentValue - Write-Host "✅ Model available - Model: $ModelType | Used: $CurrentValue | Limit: $Limit | Available: $Available" - - if ($Available -lt $Capacity) { - Write-Error "❌ ERROR: Insufficient quota for model: $Model in location: $Location. Available: $Available, Requested: $Capacity." - exit 1 - } else { - Write-Host "✅ Sufficient quota for model: $Model in location: $Location. Available: $Available, Requested: $Capacity." - } -} \ No newline at end of file diff --git a/scripts/validate_model_quota.sh b/scripts/validate_model_quota.sh deleted file mode 100644 index 79a662c..0000000 --- a/scripts/validate_model_quota.sh +++ /dev/null @@ -1,93 +0,0 @@ -#!/bin/bash - -LOCATION="" -MODEL="" -DEPLOYMENT_TYPE="Standard" -CAPACITY=0 - -while [[ $# -gt 0 ]]; do - case "$1" in - --model) - MODEL="$2" - shift 2 - ;; - --capacity) - CAPACITY="$2" - shift 2 - ;; - --deployment-type) - DEPLOYMENT_TYPE="$2" - shift 2 - ;; - --location) - LOCATION="$2" - shift 2 - ;; - *) - echo "Unknown option: $1" - exit 1 - ;; - esac -done - -# Verify all required parameters are provided and echo missing ones -MISSING_PARAMS=() - -if [[ -z "$LOCATION" ]]; then - MISSING_PARAMS+=("location") -fi - -if [[ -z "$MODEL" ]]; then - MISSING_PARAMS+=("model") -fi - -if [[ -z "$CAPACITY" ]]; then - MISSING_PARAMS+=("capacity") -fi - -if [[ -z "$DEPLOYMENT_TYPE" ]]; then - MISSING_PARAMS+=("deployment-type") -fi - -if [[ ${#MISSING_PARAMS[@]} -ne 0 ]]; then - echo "❌ ERROR: Missing required parameters: ${MISSING_PARAMS[*]}" - echo "Usage: $0 --location --model --capacity [--deployment-type ]" - exit 1 -fi - -if [[ "$DEPLOYMENT_TYPE" != "Standard" && "$DEPLOYMENT_TYPE" != "GlobalStandard" ]]; then - echo "❌ ERROR: Invalid deployment type: $DEPLOYMENT_TYPE. Allowed values are 'Standard' or 'GlobalStandard'." - exit 1 -fi - -MODEL_TYPE="OpenAI.$DEPLOYMENT_TYPE.$MODEL" - -echo "🔍 Checking quota for $MODEL_TYPE in $LOCATION ..." - -MODEL_INFO=$(az cognitiveservices usage list --location "$LOCATION" --query "[?name.value=='$MODEL_TYPE']" --output json | tr '[:upper:]' '[:lower:]') - -if [ -z "$MODEL_INFO" ]; then - echo "❌ ERROR: No quota information found for model: $MODEL in location: $LOCATION for model type: $MODEL_TYPE." - exit 1 -fi - -if [ -n "$MODEL_INFO" ]; then - CURRENT_VALUE=$(echo "$MODEL_INFO" | awk -F': ' '/"currentvalue"/ {print $2}' | tr -d ',' | tr -d ' ') - LIMIT=$(echo "$MODEL_INFO" | awk -F': ' '/"limit"/ {print $2}' | tr -d ',' | tr -d ' ') - - CURRENT_VALUE=${CURRENT_VALUE:-0} - LIMIT=${LIMIT:-0} - - CURRENT_VALUE=$(echo "$CURRENT_VALUE" | cut -d'.' -f1) - LIMIT=$(echo "$LIMIT" | cut -d'.' -f1) - - AVAILABLE=$((LIMIT - CURRENT_VALUE)) - echo "✅ Model available - Model: $MODEL_TYPE | Used: $CURRENT_VALUE | Limit: $LIMIT | Available: $AVAILABLE" - - if [ "$AVAILABLE" -lt "$CAPACITY" ]; then - echo "❌ ERROR: Insufficient quota for model: $MODEL in location: $LOCATION. Available: $AVAILABLE, Requested: $CAPACITY." - exit 1 - else - echo "✅ Sufficient quota for model: $MODEL in location: $LOCATION. Available: $AVAILABLE, Requested: $CAPACITY." - fi -fi From 1176cf420f0cdb73a187f207600643cbe37ef072 Mon Sep 17 00:00:00 2001 From: Seth Date: Tue, 1 Jul 2025 15:16:10 -0400 Subject: [PATCH 32/44] Quota - documentation and main.json --- docs/modify_deployed_models.md | 47 +++----- infra/main.json | 206 +++++++++++++-------------------- 2 files changed, 100 insertions(+), 153 deletions(-) diff --git a/docs/modify_deployed_models.md b/docs/modify_deployed_models.md index d48dc8e..906e614 100644 --- a/docs/modify_deployed_models.md +++ b/docs/modify_deployed_models.md @@ -1,37 +1,24 @@ ## Update AI Model Deployments -The AI Models that can be deployed and attached to the Foundry hub can be modified by changing the parameters within the [main.parameters.json](../infra/main.parameters.json) file. -By modifying the parameters listed in the parameters.json 'aiModelDeployments' section, additional or different models can be deployed with the solution and ready for use after the deployment. Simply modify the values to your liking in each of the objects, or add additional objects to the array. -```powershell +The AI Models that can be deployed and attached to the Foundry hub can be modified by changing the parameters within the [main.parameters.json](../infra/main.parameters.json) file. +By modifying the parameters `aiEmbeddingModelDeployment` and `aiGPTModelDeployment` listed in the parameters.json, different embedding and GPT models can be deployed with the solution and ready for use after the deployment. Simply modify the values to your liking in each of the objects. - "aiModelDeployments": { - "value": [ - { - "name": "textembed", - "model": { - "name": modelName, - "format": modelPublisherFormat, - "version": modelVersion - }, - "sku": { - "name": skuName, - "capacity": capacity - } - }, - { - "name": "gpt", - "model": { - "name": "gpt-4o", - "version": "2024-05-13", - "format": "OpenAI" - }, - "sku": { - "name": "GlobalStandard", - "capacity": 10 - } - } - ] +```powershell + "aiEmbeddingModelDeployment": { + "value": { + "modelName": modelName, + "version": modelVersion, + "capacity": capacity + } + }, + "aiGPTModelDeployment": { + "value": { + "modelName": "gpt-4o", + "version": "2024-05-13", + "capacity": 150 + } } ``` + To find and validate additional model information, the [AI Foundry](https://ai.azure.com/explore/models) model page has the above parameters to refer to, as does the Microsoft Learn page for [Azure OpenAI Service Models](https://learn.microsoft.com/en-us/azure/ai-services/openai_) information. diff --git a/infra/main.json b/infra/main.json index e2b780f..ba43322 100644 --- a/infra/main.json +++ b/infra/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "14037525013444695971" + "version": "0.33.93.31351", + "templateHash": "2701822487560513808" } }, "definitions": { @@ -591,101 +591,37 @@ } } }, - "deploymentsType": { + "modelDeploymentType": { "type": "object", "properties": { "name": { "type": "string", "nullable": true, "metadata": { - "description": "Optional. Specify the name of cognitive service account deployment." + "description": "Optional. The name of the Cognitive Services account deployment model. The modelName will be used by default if not specified." } }, - "model": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of Cognitive Services account deployment model." - } - }, - "format": { - "type": "string", - "metadata": { - "description": "Required. The format of Cognitive Services account deployment model." - } - }, - "version": { - "type": "string", - "metadata": { - "description": "Required. The version of Cognitive Services account deployment model." - } - } - }, - "metadata": { - "description": "Required. Properties of Cognitive Services account deployment model." - } - }, - "sku": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the resource model definition representing SKU." - } - }, - "capacity": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The capacity of the resource model definition representing SKU." - } - }, - "tier": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The tier of the resource model definition representing SKU." - } - }, - "size": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The size of the resource model definition representing SKU." - } - }, - "family": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The family of the resource model definition representing SKU." - } - } - }, - "nullable": true, + "modelName": { + "type": "string", "metadata": { - "description": "Optional. The resource model definition representing SKU." + "description": "Required. The format of the Cognitive Services account deployment model." } }, - "raiPolicyName": { + "version": { "type": "string", - "nullable": true, "metadata": { - "description": "Optional. The name of RAI policy." + "description": "Required. The version of the Cognitive Services account deployment model." } }, - "versionUpgradeOption": { - "type": "string", - "nullable": true, + "capacity": { + "type": "int", "metadata": { - "description": "Optional. The version upgrade option." + "description": "Required. The capacity of the resource model definition representing SKU." } } }, "metadata": { + "description": "The AI model deployment type for Cognitive Services account.", "__bicep_imported_from!": { "sourceTemplate": "modules/customTypes.bicep" } @@ -877,17 +813,35 @@ "location": { "type": "string", "metadata": { - "description": "Specifies the location for all the Azure resources. Defaults to the location of the resource group." + "azd": { + "type": "location" + }, + "description": "Specifies the location for all the Azure resources." } }, - "aiModelDeployments": { - "type": "array", - "items": { - "$ref": "#/definitions/deploymentsType" - }, - "defaultValue": [], + "aiEmbeddingModelDeployment": { + "$ref": "#/definitions/modelDeploymentType", + "metadata": { + "description": "Specifies the AI embedding model to use for the AI Foundry deployment. This is the model used for text embeddings in AI Foundry. NOTE: Any adjustments to this parameter's values must also be made on the aiDeploymentsLocation metadata in the main.bicep file." + } + }, + "aiGPTModelDeployment": { + "$ref": "#/definitions/modelDeploymentType", "metadata": { - "description": "Optional. Specifies the OpenAI deployments to create." + "description": "Specifies the AI chat model to use for the AI Foundry deployment. This is the model used for chat interactions in AI Foundry. NOTE: Any adjustments to this parameter's values must also be made on the aiDeploymentsLocation metadata in the main.bicep file." + } + }, + "aiDeploymentsLocation": { + "type": "string", + "metadata": { + "azd": { + "type": "location", + "usageName": [ + "OpenAI.GlobalStandard.gpt-4o,150", + "OpenAI.GlobalStandard.text-embedding-3-small,100" + ] + }, + "description": "Required. Location for AI Foundry deployment. This is the location where the AI Foundry resources will be deployed." } }, "acrEnabled": { @@ -4900,8 +4854,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "14424734402412352330" + "version": "0.33.93.31351", + "templateHash": "10563293765969438544" } }, "parameters": { @@ -5580,8 +5534,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "11746621349310683559" + "version": "0.33.93.31351", + "templateHash": "9442281453257963936" } }, "parameters": { @@ -11965,8 +11919,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "2158520837294746606" + "version": "0.33.93.31351", + "templateHash": "12630130910180117756" } }, "parameters": { @@ -18193,8 +18147,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "8737464205872383016" + "version": "0.33.93.31351", + "templateHash": "5079190893808934258" } }, "definitions": { @@ -30173,7 +30127,7 @@ "value": "[variables('resourceToken')]" }, "location": { - "value": "[parameters('location')]" + "value": "[parameters('aiDeploymentsLocation')]" }, "networkIsolation": { "value": "[parameters('networkIsolation')]" @@ -30187,7 +30141,13 @@ "value": "[reference('logAnalyticsWorkspace').outputs.resourceId.value]" }, "aiModelDeployments": { - "value": "[parameters('aiModelDeployments')]" + "copy": [ + { + "name": "value", + "count": "[length(createArray(parameters('aiEmbeddingModelDeployment'), parameters('aiGPTModelDeployment')))]", + "input": "[createObject('name', if(empty(tryGet(createArray(parameters('aiEmbeddingModelDeployment'), parameters('aiGPTModelDeployment'))[copyIndex('value')], 'name')), createArray(parameters('aiEmbeddingModelDeployment'), parameters('aiGPTModelDeployment'))[copyIndex('value')].modelName, tryGet(createArray(parameters('aiEmbeddingModelDeployment'), parameters('aiGPTModelDeployment'))[copyIndex('value')], 'name')), 'model', createObject('name', createArray(parameters('aiEmbeddingModelDeployment'), parameters('aiGPTModelDeployment'))[copyIndex('value')].modelName, 'format', 'OpenAI', 'version', createArray(parameters('aiEmbeddingModelDeployment'), parameters('aiGPTModelDeployment'))[copyIndex('value')].version), 'sku', createObject('name', 'GlobalStandard', 'capacity', createArray(parameters('aiEmbeddingModelDeployment'), parameters('aiGPTModelDeployment'))[copyIndex('value')].capacity))]" + } + ] }, "userObjectId": { "value": "[parameters('userObjectId')]" @@ -30221,8 +30181,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "532244891627057375" + "version": "0.33.93.31351", + "templateHash": "12413221929297508620" } }, "definitions": { @@ -36573,8 +36533,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "2348080591288311162" + "version": "0.33.93.31351", + "templateHash": "6894574649925342276" } }, "definitions": { @@ -39314,8 +39274,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "2348080591288311162" + "version": "0.33.93.31351", + "templateHash": "6894574649925342276" } }, "definitions": { @@ -42057,8 +42017,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "2348080591288311162" + "version": "0.33.93.31351", + "templateHash": "6894574649925342276" } }, "definitions": { @@ -44800,8 +44760,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "2348080591288311162" + "version": "0.33.93.31351", + "templateHash": "6894574649925342276" } }, "definitions": { @@ -47540,8 +47500,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "2348080591288311162" + "version": "0.33.93.31351", + "templateHash": "6894574649925342276" } }, "definitions": { @@ -50283,8 +50243,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "2348080591288311162" + "version": "0.33.93.31351", + "templateHash": "6894574649925342276" } }, "definitions": { @@ -53023,8 +52983,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "2348080591288311162" + "version": "0.33.93.31351", + "templateHash": "6894574649925342276" } }, "definitions": { @@ -55788,8 +55748,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "15578119539506362308" + "version": "0.33.93.31351", + "templateHash": "337781424599225735" } }, "parameters": { @@ -55994,8 +55954,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "10624928188153796868" + "version": "0.33.93.31351", + "templateHash": "10325325476075721125" } }, "definitions": { @@ -61416,8 +61376,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "14156198524686936179" + "version": "0.33.93.31351", + "templateHash": "10286244752482293080" } }, "parameters": { @@ -61985,8 +61945,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "9207373860269569676" + "version": "0.33.93.31351", + "templateHash": "13278853949319290770" } }, "parameters": { @@ -69918,8 +69878,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "7173901627480655800" + "version": "0.33.93.31351", + "templateHash": "1436217564672922666" } }, "definitions": { @@ -77117,8 +77077,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "18362011243916437863" + "version": "0.33.93.31351", + "templateHash": "5330058909966791926" } }, "definitions": { From bfaf0cb8b7b0aefe00cbcafdbb175d1dba349f31 Mon Sep 17 00:00:00 2001 From: Seth Date: Tue, 1 Jul 2025 15:18:32 -0400 Subject: [PATCH 33/44] Disabled no-unnecessary-dependson warnings --- infra/modules/aisearch.bicep | 1 + infra/modules/cognitive-services/main.bicep | 7 +++++++ infra/modules/containerRegistry.bicep | 1 + infra/modules/cosmosDb.bicep | 1 + infra/modules/keyvault.bicep | 1 + infra/modules/sqlServer.bicep | 1 + infra/modules/storageAccount.bicep | 1 + 7 files changed, 13 insertions(+) diff --git a/infra/modules/aisearch.bicep b/infra/modules/aisearch.bicep index 9a4e9a1..4995849 100644 --- a/infra/modules/aisearch.bicep +++ b/infra/modules/aisearch.bicep @@ -42,6 +42,7 @@ var nameFormatted = take(toLower(name), 60) module aiSearch 'br/public:avm/res/search/search-service:0.9.2' = { name: take('${nameFormatted}-search-services-deployment', 64) + #disable-next-line no-unnecessary-dependson dependsOn: [privateDnsZone] // required due to optional flags that could change dependency params: { name: nameFormatted diff --git a/infra/modules/cognitive-services/main.bicep b/infra/modules/cognitive-services/main.bicep index 5095191..32c7372 100644 --- a/infra/modules/cognitive-services/main.bicep +++ b/infra/modules/cognitive-services/main.bicep @@ -79,6 +79,7 @@ module openAiPrivateDnsZone 'br/public:avm/res/network/private-dns-zone:0.7.0' = module aiServices 'service.bicep' = { name: take('${name}-ai-services-deployment', 64) + #disable-next-line no-unnecessary-dependson dependsOn: [cognitiveServicesPrivateDnsZone, openAiPrivateDnsZone] // required due to optional flags that could change dependency params: { name: 'cog${name}${resourceToken}' @@ -118,6 +119,7 @@ module aiServices 'service.bicep' = { module contentSafety 'service.bicep' = if (contentSafetyEnabled) { name: take('${name}-content-safety-deployment', 64) + #disable-next-line no-unnecessary-dependson dependsOn: [cognitiveServicesPrivateDnsZone] // required due to optional flags that could change dependency params: { name: 'safety${name}${resourceToken}' @@ -136,6 +138,7 @@ module contentSafety 'service.bicep' = if (contentSafetyEnabled) { module vision 'service.bicep' = if (visionEnabled) { name: take('${name}-vision-deployment', 64) + #disable-next-line no-unnecessary-dependson dependsOn: [cognitiveServicesPrivateDnsZone] // required due to optional flags that could change dependency params: { name: 'vision${name}${resourceToken}' @@ -155,6 +158,7 @@ module vision 'service.bicep' = if (visionEnabled) { module language 'service.bicep' = if (languageEnabled) { name: take('${name}-language-deployment', 64) + #disable-next-line no-unnecessary-dependson dependsOn: [cognitiveServicesPrivateDnsZone] // required due to optional flags that could change dependency params: { name: 'lang${name}${resourceToken}' @@ -174,6 +178,7 @@ module language 'service.bicep' = if (languageEnabled) { module speech 'service.bicep' = if (speechEnabled) { name: take('${name}-speech-deployment', 64) + #disable-next-line no-unnecessary-dependson dependsOn: [cognitiveServicesPrivateDnsZone] // required due to optional flags that could change dependency params: { name: 'speech${name}${resourceToken}' @@ -192,6 +197,7 @@ module speech 'service.bicep' = if (speechEnabled) { module translator 'service.bicep' = if (translatorEnabled) { name: take('${name}-translator-deployment', 64) + #disable-next-line no-unnecessary-dependson dependsOn: [cognitiveServicesPrivateDnsZone] // required due to optional flags that could change dependency params: { name: 'translator${name}${resourceToken}' @@ -211,6 +217,7 @@ module translator 'service.bicep' = if (translatorEnabled) { module documentIntelligence 'service.bicep' = if (documentIntelligenceEnabled) { name: take('${name}-doc-intel-deployment', 64) + #disable-next-line no-unnecessary-dependson dependsOn: [cognitiveServicesPrivateDnsZone] // required due to optional flags that could change dependency params: { name: 'docintel${name}${resourceToken}' diff --git a/infra/modules/containerRegistry.bicep b/infra/modules/containerRegistry.bicep index 3cb5046..216b82c 100644 --- a/infra/modules/containerRegistry.bicep +++ b/infra/modules/containerRegistry.bicep @@ -37,6 +37,7 @@ var nameFormatted = take(toLower(name), 50) module containerRegistry 'br/public:avm/res/container-registry/registry:0.8.4' = { name: take('${nameFormatted}-container-registry-deployment', 64) + #disable-next-line no-unnecessary-dependson dependsOn: [privateDnsZone] // required due to optional flags that could change dependency params: { name: nameFormatted diff --git a/infra/modules/cosmosDb.bicep b/infra/modules/cosmosDb.bicep index 9e5afd5..d4f30c7 100644 --- a/infra/modules/cosmosDb.bicep +++ b/infra/modules/cosmosDb.bicep @@ -42,6 +42,7 @@ var nameFormatted = toLower(name) module cosmosDb 'br/public:avm/res/document-db/database-account:0.11.0' = { name: take('${nameFormatted}-cosmosdb-deployment', 64) + #disable-next-line no-unnecessary-dependson dependsOn: [privateDnsZone] // required due to optional flags that could change dependency params: { name: nameFormatted diff --git a/infra/modules/keyvault.bicep b/infra/modules/keyvault.bicep index 79bc2ea..1a0f1fe 100644 --- a/infra/modules/keyvault.bicep +++ b/infra/modules/keyvault.bicep @@ -40,6 +40,7 @@ var nameFormatted = take(toLower(name), 24) module keyvault 'br/public:avm/res/key-vault/vault:0.11.0' = { name: take('${nameFormatted}-keyvault-deployment', 64) + #disable-next-line no-unnecessary-dependson dependsOn: [privateDnsZone] // required due to optional flags that could change dependency params: { name: nameFormatted diff --git a/infra/modules/sqlServer.bicep b/infra/modules/sqlServer.bicep index 8209859..628972d 100644 --- a/infra/modules/sqlServer.bicep +++ b/infra/modules/sqlServer.bicep @@ -46,6 +46,7 @@ var nameFormatted = toLower(name) module sqlServer 'br/public:avm/res/sql/server:0.15.0' = { name: take('${nameFormatted}-sqlserver-deployment', 64) + #disable-next-line no-unnecessary-dependson dependsOn: [privateDnsZone] // required due to optional flags that could change dependency params: { name: nameFormatted diff --git a/infra/modules/storageAccount.bicep b/infra/modules/storageAccount.bicep index f9aa42b..dbca770 100644 --- a/infra/modules/storageAccount.bicep +++ b/infra/modules/storageAccount.bicep @@ -52,6 +52,7 @@ var nameFormatted = take(toLower(storageName), 24) module storageAccount 'br/public:avm/res/storage/storage-account:0.17.0' = { name: take('${nameFormatted}-storage-account-deployment', 64) + #disable-next-line no-unnecessary-dependson dependsOn: [filePrivateDnsZone, blobPrivateDnsZone] // required due to optional flags that could change dependency params: { name: nameFormatted From 92e2bcdbd90a17d4d19a347fcebcaf6db9aab29e Mon Sep 17 00:00:00 2001 From: Seth Date: Tue, 1 Jul 2025 15:29:13 -0400 Subject: [PATCH 34/44] Search - fixed bug where roleassignments was not being used --- infra/main.bicep | 6 ++- infra/main.json | 91 +++++++++++++++++------------------- infra/modules/aisearch.bicep | 23 ++------- 3 files changed, 52 insertions(+), 68 deletions(-) diff --git a/infra/main.bicep b/infra/main.bicep index a3b70f0..8eea449 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -258,13 +258,17 @@ module aiSearch 'modules/aisearch.bicep' = if (searchEnabled) { virtualNetworkResourceId: networkIsolation ? network.outputs.virtualNetworkId : '' virtualNetworkSubnetResourceId: networkIsolation ? network.outputs.vmSubnetId : '' logAnalyticsWorkspaceResourceId: logAnalyticsWorkspace.outputs.resourceId - userObjectId: userObjectId roleAssignments: union(empty(userObjectId) ? [] : [ { principalId: userObjectId principalType: 'User' roleDefinitionIdOrName: 'Search Index Data Contributor' } + { + principalId: userObjectId + principalType: 'User' + roleDefinitionIdOrName: 'Search Index Data Reader' + } ], [ { principalId: cognitiveServices.outputs.aiServicesSystemAssignedMIPrincipalId diff --git a/infra/main.json b/infra/main.json index e2b780f..8f5ed32 100644 --- a/infra/main.json +++ b/infra/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "14037525013444695971" + "version": "0.33.93.31351", + "templateHash": "5303146368545342618" } }, "definitions": { @@ -4900,8 +4900,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "14424734402412352330" + "version": "0.33.93.31351", + "templateHash": "10563293765969438544" } }, "parameters": { @@ -5580,8 +5580,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "11746621349310683559" + "version": "0.33.93.31351", + "templateHash": "9442281453257963936" } }, "parameters": { @@ -11965,8 +11965,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "2158520837294746606" + "version": "0.33.93.31351", + "templateHash": "12630130910180117756" } }, "parameters": { @@ -18193,8 +18193,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "8737464205872383016" + "version": "0.33.93.31351", + "templateHash": "5079190893808934258" } }, "definitions": { @@ -30221,8 +30221,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "532244891627057375" + "version": "0.33.93.31351", + "templateHash": "12413221929297508620" } }, "definitions": { @@ -36573,8 +36573,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "2348080591288311162" + "version": "0.33.93.31351", + "templateHash": "6894574649925342276" } }, "definitions": { @@ -39314,8 +39314,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "2348080591288311162" + "version": "0.33.93.31351", + "templateHash": "6894574649925342276" } }, "definitions": { @@ -42057,8 +42057,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "2348080591288311162" + "version": "0.33.93.31351", + "templateHash": "6894574649925342276" } }, "definitions": { @@ -44800,8 +44800,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "2348080591288311162" + "version": "0.33.93.31351", + "templateHash": "6894574649925342276" } }, "definitions": { @@ -47540,8 +47540,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "2348080591288311162" + "version": "0.33.93.31351", + "templateHash": "6894574649925342276" } }, "definitions": { @@ -50283,8 +50283,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "2348080591288311162" + "version": "0.33.93.31351", + "templateHash": "6894574649925342276" } }, "definitions": { @@ -53023,8 +53023,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "2348080591288311162" + "version": "0.33.93.31351", + "templateHash": "6894574649925342276" } }, "definitions": { @@ -55788,8 +55788,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "15578119539506362308" + "version": "0.33.93.31351", + "templateHash": "337781424599225735" } }, "parameters": { @@ -55977,11 +55977,8 @@ "logAnalyticsWorkspaceResourceId": { "value": "[reference('logAnalyticsWorkspace').outputs.resourceId.value]" }, - "userObjectId": { - "value": "[parameters('userObjectId')]" - }, "roleAssignments": { - "value": "[union(if(empty(parameters('userObjectId')), createArray(), createArray(createObject('principalId', parameters('userObjectId'), 'principalType', 'User', 'roleDefinitionIdOrName', 'Search Index Data Contributor'))), createArray(createObject('principalId', reference('cognitiveServices').outputs.aiServicesSystemAssignedMIPrincipalId.value, 'principalType', 'ServicePrincipal', 'roleDefinitionIdOrName', 'Search Index Data Contributor'), createObject('principalId', reference('cognitiveServices').outputs.aiServicesSystemAssignedMIPrincipalId.value, 'principalType', 'ServicePrincipal', 'roleDefinitionIdOrName', 'Search Service Contributor')))]" + "value": "[union(if(empty(parameters('userObjectId')), createArray(), createArray(createObject('principalId', parameters('userObjectId'), 'principalType', 'User', 'roleDefinitionIdOrName', 'Search Index Data Contributor'), createObject('principalId', parameters('userObjectId'), 'principalType', 'User', 'roleDefinitionIdOrName', 'Search Index Data Reader'))), createArray(createObject('principalId', reference('cognitiveServices').outputs.aiServicesSystemAssignedMIPrincipalId.value, 'principalType', 'ServicePrincipal', 'roleDefinitionIdOrName', 'Search Index Data Contributor'), createObject('principalId', reference('cognitiveServices').outputs.aiServicesSystemAssignedMIPrincipalId.value, 'principalType', 'ServicePrincipal', 'roleDefinitionIdOrName', 'Search Service Contributor')))]" }, "tags": { "value": "[variables('allTags')]" @@ -55994,8 +55991,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "10624928188153796868" + "version": "0.33.93.31351", + "templateHash": "9060803724389588162" } }, "definitions": { @@ -56120,12 +56117,6 @@ "description": "Specifies whether network isolation is enabled. This will create a private endpoint for the AI Search resource and link the private DNS zone." } }, - "userObjectId": { - "type": "string", - "metadata": { - "description": "Specifies the object id of a Microsoft Entra ID user. In general, this the object id of the system administrator who deploys the Azure resources. This defaults to the deploying user." - } - }, "roleAssignments": { "type": "array", "items": { @@ -59221,7 +59212,9 @@ "replicaCount": { "value": 3 }, - "roleAssignments": "[if(empty(parameters('userObjectId')), createObject('value', createArray()), createObject('value', createArray(createObject('principalId', parameters('userObjectId'), 'principalType', 'User', 'roleDefinitionIdOrName', 'Search Index Data Contributor'), createObject('principalId', parameters('userObjectId'), 'principalType', 'User', 'roleDefinitionIdOrName', 'Search Index Data Reader'))))]", + "roleAssignments": { + "value": "[parameters('roleAssignments')]" + }, "diagnosticSettings": { "value": [ { @@ -61416,8 +61409,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "14156198524686936179" + "version": "0.33.93.31351", + "templateHash": "10286244752482293080" } }, "parameters": { @@ -61985,8 +61978,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "9207373860269569676" + "version": "0.33.93.31351", + "templateHash": "13278853949319290770" } }, "parameters": { @@ -69918,8 +69911,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "7173901627480655800" + "version": "0.33.93.31351", + "templateHash": "1436217564672922666" } }, "definitions": { @@ -77117,8 +77110,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "18362011243916437863" + "version": "0.33.93.31351", + "templateHash": "5330058909966791926" } }, "definitions": { diff --git a/infra/modules/aisearch.bicep b/infra/modules/aisearch.bicep index 9a4e9a1..1e2fb11 100644 --- a/infra/modules/aisearch.bicep +++ b/infra/modules/aisearch.bicep @@ -19,9 +19,7 @@ param logAnalyticsWorkspaceResourceId string @description('Specifies whether network isolation is enabled. This will create a private endpoint for the AI Search resource and link the private DNS zone.') param networkIsolation bool = true -@description('Specifies the object id of a Microsoft Entra ID user. In general, this the object id of the system administrator who deploys the Azure resources. This defaults to the deploying user.') -param userObjectId string - +import { roleAssignmentType } from 'br/public:avm/utl/types/avm-common-types:0.5.1' @description('Optional. Array of role assignments to create.') param roleAssignments roleAssignmentType[]? @@ -53,20 +51,9 @@ module aiSearch 'br/public:avm/res/search/search-service:0.9.2' = { publicNetworkAccess: networkIsolation ? 'Disabled' : 'Enabled' disableLocalAuth: true sku: 'standard' - partitionCount:1 - replicaCount:3 - roleAssignments: empty(userObjectId) ? [] : [ - { - principalId: userObjectId - principalType: 'User' - roleDefinitionIdOrName: 'Search Index Data Contributor' - } - { - principalId: userObjectId - principalType: 'User' - roleDefinitionIdOrName: 'Search Index Data Reader' - } - ] + partitionCount: 1 + replicaCount: 3 + roleAssignments: roleAssignments diagnosticSettings: [ { workspaceResourceId: logAnalyticsWorkspaceResourceId @@ -88,7 +75,7 @@ module aiSearch 'br/public:avm/res/search/search-service:0.9.2' = { } } -import { roleAssignmentType } from 'br/public:avm/utl/types/avm-common-types:0.5.1' + output resourceId string = aiSearch.outputs.resourceId output name string = aiSearch.outputs.name From c157ffb401581cffdf5104cebbae6ab76eb17b7e Mon Sep 17 00:00:00 2001 From: Prajwal D C Date: Thu, 3 Jul 2025 23:00:07 +0530 Subject: [PATCH 35/44] fix: AI project location to be same as Congnitive service account --- infra/main.bicep | 2 +- infra/main.json | 78 ++++++++++++++++++++++++------------------------ 2 files changed, 40 insertions(+), 40 deletions(-) diff --git a/infra/main.bicep b/infra/main.bicep index 62cb4ab..a16fcd2 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -265,7 +265,7 @@ module project 'modules/ai-foundry-project/main.bicep' = { cosmosDbEnabled: cosmosDbEnabled searchEnabled: searchEnabled name: projectName - location: location + location: aiDeploymentsLocation storageName: storageAccount.outputs.storageName aiServicesName: cognitiveServices.outputs.aiServicesName nameFormatted: searchEnabled ? aiSearch.outputs.name : '' diff --git a/infra/main.json b/infra/main.json index ba43322..87482e9 100644 --- a/infra/main.json +++ b/infra/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "2701822487560513808" + "version": "0.36.1.42791", + "templateHash": "1266095135796943159" } }, "definitions": { @@ -4854,8 +4854,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "10563293765969438544" + "version": "0.36.1.42791", + "templateHash": "14424734402412352330" } }, "parameters": { @@ -5534,8 +5534,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "9442281453257963936" + "version": "0.36.1.42791", + "templateHash": "11746621349310683559" } }, "parameters": { @@ -11919,8 +11919,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "12630130910180117756" + "version": "0.36.1.42791", + "templateHash": "2158520837294746606" } }, "parameters": { @@ -18147,8 +18147,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "5079190893808934258" + "version": "0.36.1.42791", + "templateHash": "8737464205872383016" } }, "definitions": { @@ -30181,8 +30181,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "12413221929297508620" + "version": "0.36.1.42791", + "templateHash": "532244891627057375" } }, "definitions": { @@ -36533,8 +36533,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "6894574649925342276" + "version": "0.36.1.42791", + "templateHash": "2348080591288311162" } }, "definitions": { @@ -39274,8 +39274,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "6894574649925342276" + "version": "0.36.1.42791", + "templateHash": "2348080591288311162" } }, "definitions": { @@ -42017,8 +42017,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "6894574649925342276" + "version": "0.36.1.42791", + "templateHash": "2348080591288311162" } }, "definitions": { @@ -44760,8 +44760,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "6894574649925342276" + "version": "0.36.1.42791", + "templateHash": "2348080591288311162" } }, "definitions": { @@ -47500,8 +47500,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "6894574649925342276" + "version": "0.36.1.42791", + "templateHash": "2348080591288311162" } }, "definitions": { @@ -50243,8 +50243,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "6894574649925342276" + "version": "0.36.1.42791", + "templateHash": "2348080591288311162" } }, "definitions": { @@ -52983,8 +52983,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "6894574649925342276" + "version": "0.36.1.42791", + "templateHash": "2348080591288311162" } }, "definitions": { @@ -55732,7 +55732,7 @@ "value": "[parameters('projectName')]" }, "location": { - "value": "[parameters('location')]" + "value": "[parameters('aiDeploymentsLocation')]" }, "storageName": { "value": "[reference('storageAccount').outputs.storageName.value]" @@ -55748,8 +55748,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "337781424599225735" + "version": "0.36.1.42791", + "templateHash": "15578119539506362308" } }, "parameters": { @@ -55954,8 +55954,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "10325325476075721125" + "version": "0.36.1.42791", + "templateHash": "10624928188153796868" } }, "definitions": { @@ -61376,8 +61376,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "10286244752482293080" + "version": "0.36.1.42791", + "templateHash": "14156198524686936179" } }, "parameters": { @@ -61945,8 +61945,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "13278853949319290770" + "version": "0.36.1.42791", + "templateHash": "9207373860269569676" } }, "parameters": { @@ -69878,8 +69878,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "1436217564672922666" + "version": "0.36.1.42791", + "templateHash": "7173901627480655800" } }, "definitions": { @@ -77077,8 +77077,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "5330058909966791926" + "version": "0.36.1.42791", + "templateHash": "18362011243916437863" } }, "definitions": { From 02a6875604fed58d819c55bb774d68db9dd32a0c Mon Sep 17 00:00:00 2001 From: Vamshi-Microsoft Date: Wed, 9 Jul 2025 05:54:36 +0000 Subject: [PATCH 36/44] add broken link checker workflow for markdown files --- .github/workflows/broken-links-checker.yml | 57 ++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 .github/workflows/broken-links-checker.yml diff --git a/.github/workflows/broken-links-checker.yml b/.github/workflows/broken-links-checker.yml new file mode 100644 index 0000000..9901fa1 --- /dev/null +++ b/.github/workflows/broken-links-checker.yml @@ -0,0 +1,57 @@ +name: Broken Link Checker + +on: + pull_request: + paths: + - '**/*.md' + workflow_dispatch: + +permissions: + contents: read + +jobs: + markdown-link-check: + name: Check Markdown Broken Links + runs-on: ubuntu-latest + + steps: + - name: Checkout Repo + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + # For PR : Get only changed markdown files + - name: Get changed markdown files (PR only) + id: changed-markdown-files + if: github.event_name == 'pull_request' + uses: tj-actions/changed-files@ed68ef82c095e0d48ec87eccea555d944a631a4c # v46 + with: + files: | + **/*.md + + + # For PR: Check broken links only in changed files + - name: Check Broken Links in Changed Markdown Files + id: lychee-check-pr + if: github.event_name == 'pull_request' && steps.changed-markdown-files.outputs.any_changed == 'true' + uses: lycheeverse/lychee-action@v2.4.1 + with: + args: > + --verbose --exclude-mail --no-progress --exclude ^https?:// + ${{ steps.changed-markdown-files.outputs.all_changed_files }} + failIfEmpty: false + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + # For manual trigger: Check all markdown files in repo + - name: Check Broken Links in All Markdown Files in Entire Repo (Manual Trigger) + id: lychee-check-manual + if: github.event_name == 'workflow_dispatch' + uses: lycheeverse/lychee-action@v2.4.1 + with: + args: > + --verbose --exclude-mail --no-progress --exclude ^https?:// + '**/*.md' + failIfEmpty: false + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file From e718f9c35f9db153f0c5cc1a64c6de5f62ad69de Mon Sep 17 00:00:00 2001 From: Vamshi-Microsoft Date: Wed, 9 Jul 2025 09:20:02 +0000 Subject: [PATCH 37/44] update file paths for testing script and images in documentation --- docs/Verify_Services_On_Network.md | 2 +- docs/quota_check.md | 2 +- img/provisioning/git_bash.png | Bin 0 -> 28663 bytes 3 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 img/provisioning/git_bash.png diff --git a/docs/Verify_Services_On_Network.md b/docs/Verify_Services_On_Network.md index b6d2be8..f296193 100644 --- a/docs/Verify_Services_On_Network.md +++ b/docs/Verify_Services_On_Network.md @@ -6,7 +6,7 @@ This guide will walk you through using a secure jump-box virtual machine to inst ### 1. Copy Testing Script to Virtual Machine -Copy [test_azure_resource_conns.ps1](./scripts/test_azure_resource_conns.ps1) to the Virtual Machine. +Copy [test_azure_resource_conns.ps1](../scripts/test_azure_resource_conns.ps1) to the Virtual Machine. ### 2. Install Azure CLI diff --git a/docs/quota_check.md b/docs/quota_check.md index e265b12..94320a7 100644 --- a/docs/quota_check.md +++ b/docs/quota_check.md @@ -75,7 +75,7 @@ The final table lists regions with available quota. You can select any of these ## **If using VS Code or Codespaces** 1. Open the terminal in VS Code or Codespaces. 2. If you're using VS Code, click the dropdown on the right side of the terminal window, and select `Git Bash`. - ![git_bash](Images/git_bash.png) + ![git_bash](../img/provisioning/git_bash.png) 3. Navigate to the `scripts` folder where the script files are located and make the script as executable: ```sh cd scripts diff --git a/img/provisioning/git_bash.png b/img/provisioning/git_bash.png new file mode 100644 index 0000000000000000000000000000000000000000..92dd188c9c61425fda0b0c1205897cf7ac25ad8b GIT binary patch literal 28663 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 YU&&YQcgFC- Date: Thu, 10 Jul 2025 10:36:28 -0400 Subject: [PATCH 38/44] Sample Application (#49) * App Sample - init commit, app servce bicep, bicep cleanup, auth scripts * Sample app - auth automation * App Sample - bicep updates and cleanup WIP * Sample app - removed index creation from infra, workspace cleanup, private conn fixes * Sample app - added initial docs, infra cleanup * App - setup instructions * App - Virtual Network refactoring, key vault purge fix * App - added secrets to keyvault. doc updates * App - documentation updates * App - minor docs update * App - azure.yaml update for multiple hook scripts for quota and auth_init * Rebuilt main.json. added warning lint removal for dependson * feat: Added Data ingestion script & fixes for Sample App (#63) * add cognitive avm content change api to preview remove hub and project from foundry folder add cognitive module calling the revised avm content. * update to api version added project to avm module * remove aihub aiproject * update name outputs for project * update parameter outputs * removed duplicate cognitive added proj name output * updates to deployment and service connections * update errors for proj * add location * location added to project * add preview to api for project * update projname * update name length * update length of project name * remove connection * name changed * pass cognitive name to existing * update cognitive name * storage connection * update project * add additional parameters * add search connection to project * update project api * update search resource name * update main * update storage connection name * storage account existing connection added * update storage account info * update storage api * storage name * update the storage name * update category to storage * update storage connection category to AzureBlob * update storage name * add cosmos (without flag check) add default container for storage * update cosmos settings * update cosmosdb parameters in project * cosmosdb parameters * add api version metadata * cosmosdb connection api set to 04-01 * remove cosmos references * add cosmos * apply disable local auth to project * adding network acl parameter to aiservicesaccount * change property to deny from null * update CKM enforcement to disabled * update the network acl parameter to allow azure services, and to add the subnet rule. * Add cogntive service user to aiServices module by default. This solves the inability to reach the index from within AI Foundry * Update role assignments for search service. Add Search Index contributor and Search Index Reader to user * Added additional role to user for Cognitive Services Contributor. Control Plane API call access * Add AI Developer role to user * Add UserObjectID parameter to aisearch module in main.bicep * add the AI Developer role by id and not name * update the role definition ID for AI Developer * removal of the Azure AI Developer role provisioning * added dependency for project connection. for search * add searchEnabled param to main * remove cognitive * update cosmos db api * update if search enabled * Add Azure AI Developer role name * remove cosmosdb * remove existing cosmosdb * restore cosmos db * update cosmosdb api * Add conditional checks for cosmos * update cosmosdbName * update cosmosdb name cosmosDBname * update parameter name cosmosDBname * change search service dns to private and not public url * changed it back to normal endpoint public for resolution * add private end point to project * update vnet parameters for project * added conditional check to search name * add one conditional check to search in project deployment line 251 * update private end point parameter * update vm and subnet name parameters * update network parameters * update subnet id for project pep * update the private link connection for project * revert back to foundry * update to readme for 1RP * update change log with updates * Add additional documentation * Readme updates related to FDP updates * Updates to configuration documentation * removed mention of one-click deploy * Add links to the new resources in cognitive services * update infra dwg update links * update to network acl * update to project creation * testing of AVM modle pr * remove project parameters * Update to avm pr code * update the image path * update defaultName * fix: Fixed issues when networkisolation flag is set to false * feat: Added Secrets to keyvault which would be used to run the data ingestion script & fixed the issue related to authentication * feat: Added scripts to chunck and ingest data into azure search service * fix: Removed unused param * feat: Added the Roles required to execute the script through VM * fix: Added role assignments & identity for vm * feat: Added changes for running script through VM * fix: Added missing role assignments * fix: Updated scripts to test VM * fix: Updated powershell code * fix: Added Logs * fix: Added more loga * refactor: Different approach to ingest data * replace the in place module with the avm pattern module path * fix: Updated script * fix: Seggregated installtion of python * fix: Created diff scripts * fix: Refresh environment variables * fix: Collecting python logs * fix: Removed logs on python * fix: Added logs * fix: Updated logic for python installation * fix: Use extarcted python package * fix: test dns resolution * fic: Updated host * fix: Updated host * fix: Updated host * fix: Deployment Issue with AZD version 1.17 fixed * feat: Updated the main.json * docs: Added Note related to AZD issues with 1.17.0 version * corrected readme * fix: fixed Project name to 12 characters * updates to remove AML calls to import. * fix: Updated scripts * Quota - implemented AZD quota check, removed model array for embedding and gpt only, removed scripts * Quota - documentation and main.json * Disabled no-unnecessary-dependson warnings * Search - fixed bug where roleassignments was not being used * refactor: Modified the output variables * fix: Resolved the conflict issues * fix: Sample APP deployment issue * fix: AI project location to be same as Congnitive service account * feat: Updated boolean based flags to propmpy user for inputs as AZD issue is fixed * fix: Added the changes related to auth update * fix: Added python path * fix: Updated python script to download the file to specific path * fix: Updated the message * fix: Added sample data & updated script to support non-waf deployment * fix: Fix issues related to codespace * chore: Updated the sample data set * fix: Modified code to process files from local folder while running locally * fix: Updated scripts for Codespace * fix: Updated the path * fix: File names updated * fix: File names updated --------- Co-authored-by: mswantek68 <46489667+mswantek68@users.noreply.github.com> Co-authored-by: Mike Swantek Co-authored-by: Rohini-Microsoft Co-authored-by: Seth --------- --- .gitignore | 2 +- README.md | 3 + azure.yaml | 59 +- data/PerksPlus.pdf | Bin 0 -> 115310 bytes data/employee_handbook.pdf | Bin 0 -> 142977 bytes docs/sample_app_setup.md | 84 + infra/main.bicep | 192 +- infra/main.json | 131515 ++++++++------- infra/main.parameters.json | 23 +- infra/modules/ai-foundry-project/main.bicep | 2 - infra/modules/aisearch.bicep | 6 +- infra/modules/appservice.bicep | 316 + infra/modules/cognitive-services/main.bicep | 53 +- infra/modules/cosmosDb.bicep | 18 +- infra/modules/customTypes.bicep | 1 - infra/modules/keyvault.bicep | 24 +- infra/modules/storageAccount.bicep | 1 + infra/modules/virtualMachine.bicep | 4 + infra/modules/virtualNetwork.bicep | 366 +- infra/modules/vmscriptsetup.bicep | 101 + requirements-dev.txt | 1 + requirements.txt | 1 + scripts/auth_init.ps1 | 16 + scripts/auth_init.py | 93 + scripts/auth_init.sh | 11 + scripts/auth_update.py | 52 + .../index_scripts/01_create_search_index.py | 72 + scripts/index_scripts/02_process_data.py | 179 + scripts/index_scripts/requirements.txt | 10 + scripts/install_python.ps1 | 22 + scripts/loadenv.ps1 | 64 + scripts/loadenv.sh | 34 + scripts/postprovision.ps1 | 33 + scripts/postprovision.sh | 30 + scripts/process_sample_data.ps1 | 98 + scripts/process_sample_data.sh | 63 + scripts/set_conns_env_vars.ps1 | 36 +- scripts/set_conns_env_vars.sh | 36 +- 38 files changed, 73577 insertions(+), 60044 deletions(-) create mode 100644 data/PerksPlus.pdf create mode 100644 data/employee_handbook.pdf create mode 100644 docs/sample_app_setup.md create mode 100644 infra/modules/appservice.bicep create mode 100644 infra/modules/vmscriptsetup.bicep create mode 100644 requirements-dev.txt create mode 100644 requirements.txt create mode 100644 scripts/auth_init.ps1 create mode 100644 scripts/auth_init.py create mode 100755 scripts/auth_init.sh create mode 100644 scripts/auth_update.py create mode 100644 scripts/index_scripts/01_create_search_index.py create mode 100644 scripts/index_scripts/02_process_data.py create mode 100644 scripts/index_scripts/requirements.txt create mode 100644 scripts/install_python.ps1 create mode 100644 scripts/loadenv.ps1 create mode 100755 scripts/loadenv.sh create mode 100644 scripts/postprovision.ps1 create mode 100755 scripts/postprovision.sh create mode 100644 scripts/process_sample_data.ps1 create mode 100755 scripts/process_sample_data.sh mode change 100644 => 100755 scripts/set_conns_env_vars.sh diff --git a/.gitignore b/.gitignore index 48a5992..16fc174 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ .vscode .vs .venv -__pycache__ \ No newline at end of file +__pycache__ diff --git a/README.md b/README.md index cbc72cb..822f66d 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,7 @@ Offers ability to [start with an existing Azure AI Project](docs/transfer_projec 4. If deploying from your [local environment](docs/local_environment_steps.md), install the [Azure CLI (AZ)](https://learn.microsoft.com/en-us/cli/azure/install-azure-cli?view=azure-cli-latest) and the [Azure Developer CLI (AZD)](https://learn.microsoft.com/en-us/azure/developer/azure-developer-cli/install-azd?tabs=winget-windows%2Cbrew-mac%2Cscript-linux&pivots=os-windows). 5. If deploying via [GitHub Codespaces](docs/github_code_spaces_steps.md) - requires the user to be on a GitHub Team or Enterprise Cloud plan. 6. If leveraging [GitHub Actions](docs/github_actions_steps.md). +7. Optionally [include a sample AI chat application](/docs/sample_app_setup.md) with the deployment. ### Check Azure OpenAI Quota Availability @@ -90,6 +91,8 @@ QUICK DEPLOY ## Connect to and validate access to the new environment Follow the post deployment steps [Post Deployment Steps](docs/github_code_spaces_steps.md) to connect to the isolated environment. +## Deploy Sample Application with the new environment +Optionally include a [sample AI chat application](/docs/sample_app_setup.md) to showcase a production AI application deployed to a secure environment. ## Deploy your application in the isolated environment - Leverage the Microsoft Learn documentation to provision an app service instance within your secure network [Configure Web App](https://learn.microsoft.com/en-us/azure/ai-services/openai/how-to/on-your-data-configuration#azure-ai-foundry-portal) diff --git a/azure.yaml b/azure.yaml index d733b84..88d7f06 100644 --- a/azure.yaml +++ b/azure.yaml @@ -1,17 +1,42 @@ -name: deploy-your-ai-application-in-production -infra: - provider: "bicep" -metadata: - template: deploy-your-ai-application-in-production@1.0 -hooks: - preup: - windows: - shell: pwsh - run: ./scripts/set_conns_env_vars.ps1 - interactive: true - continueOnError: false - posix: - shell: sh - run: chmod u+r+x ./scripts/set_conns_env_vars.sh; ./scripts/set_conns_env_vars.sh - interactive: true - continueOnError: false \ No newline at end of file +name: deploy-your-ai-application-in-production + +requiredVersions: + azd: ">=1.15.0 !=1.17.1" +infra: + provider: "bicep" +metadata: + template: deploy-your-ai-application-in-production@1.0 +hooks: + preup: + windows: + shell: pwsh + run: ./scripts/set_conns_env_vars.ps1 + interactive: true + continueOnError: false + posix: + shell: sh + run: sudo chmod u+r+x ./scripts/set_conns_env_vars.sh; sudo ./scripts/set_conns_env_vars.sh + interactive: true + continueOnError: false + preprovision: + posix: + shell: sh + run: sudo chmod u+r+x ./scripts/auth_init.sh; sudo ./scripts/auth_init.sh + interactive: true + continueOnError: false + windows: + shell: pwsh + run: ./scripts/auth_init.ps1 + interactive: true + continueOnError: false + postprovision: + posix: + shell: sh + run: sudo chmod u+r+x ./scripts/process_sample_data.sh; sudo chmod u+r+x ./scripts/postprovision.sh; sudo ./scripts/postprovision.sh + interactive: true + continueOnError: false + windows: + shell: pwsh + run: ./scripts/postprovision.ps1; + interactive: true + continueOnError: false diff --git a/data/PerksPlus.pdf b/data/PerksPlus.pdf new file mode 100644 index 0000000000000000000000000000000000000000..2e167a2a6a109d8335bf67033e2963f250d51d8b GIT binary patch literal 115310 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

NO8D)JQea<7we=>U1{1PIbd7+VN=M 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

Imlpx{e{EQ| zOr6;g+W>~|U#7YAc8oki|I!e}eOHu34imyidV~n&DG(n}ALO`xYBUqq96%PA1MJ!0 zsy^hZcAD(8V~Ic{J9oxI72Q~VtpR9zpk1eq=q`&qV+fT8Orx>g85@+SutACR^dezo zR-~<1(hmW+A$Kz3^gWyn%B#!lZL2gvf4_{@QBX*cE^A%9CWsYJ!)y4%i@n%#0w5uQIAF;v1V zP>6LR?m?C%?;bwtK##Am5uE>=ofsco0G~u#OMAkY72sOpgCXdmoJmY zeAlRNx8_uN#sB&8XJY#Ql?)~ZRyL0RnHfexb_O;!*8e>@=iV?*N=vPmn`ZG9?)Wro z@ibb=v@5sVMq~Bd{$vK;h*`1p0g1k<~W^vEV_~ zm`LEm$Hq6CcQCuKA_-_xSkxxRzA^VKEKU#5DROY``A4*cJK!}laKY>w)z3UbCIwC;?PJs^m@3zLZHd8mJ(RNU=FwsWC6$0r0 za6zF6pIyG6Od+<+9V09TZ?q z75Vs~D;UhPDuK{e!>|WOgR*tSi%&C+7PH)=53Fb}r^R`4X(!kshr$oITWzaPcVZ4i>i4alVVlF9+-pDphR5b{{eQEdd) zW*9T~LZyHLfuPUV)(^JD8gKKVrWqnv1DhtWf{1;C)XIPEyAC1GbjOC&z$z*kqfxaa zK8`^ugyY`vChP#*@u8VFg!!T^74R$vZ`fhz2RfgLyfFE~*x%WF`nRe;2P`E@Gg}t< z)0KWl9eaoZ&7yE^8BVuOdP_|QWX9J{$RE$RZkcn<86Z?Q(Px?WEUv=)9N1R^MoN4^ zSl%h0x2S3#9N6U6tZz5naH7a@#l-2~NM(s;WWEhpM=rS)#=7Ax3Zu*rmnLjIl?F91 zq=(|CeDVg}v2Gg(-(ZG>cqvG9Q~CsZrHJ&1Npzd@1$Uj>CB&bJhS}g?2%@_nkY_k6 zXoP)25V~PrYR$pBe#tT;fR)<|Dw-y2L82n#V0?pj3F72JZ>HB=UR<4BpU1Q=sV=W9 zt%6lnrJ4F4Z{`rN zetg9V)3@6rUo>Hot~Vd)axeL*ZOG;SUR@w6c^4c_sK+o0dpr($nBreKa)gzgzUKL^ z1=H?yQsbrZ#`1zvV11{rzRg{BC6xzDZ1S;RmQ|wdFRq{=&I=Mv6C5kZ&s}wyX>xX}=WZiU1#U;oSsQGesnpGJ%EY6!6@pqZ6dQqPZoC*BnB(|Y{ zfeU^lGb#!bIqBlaN%=G4IARBeQo)R2QKYzcG>8?pXeT2Soi>_5bN|=@p{-3vU&v2H zCy?!@m2US0Ue)1ap-x9rzUnMTHQ7DJ)V+w5K^YW$&8AhvNn$nI^+THFb=&2jdb5%C zspTamIiNic0z`d~s+i&e7?xqA{~Z~EcnsWbt5_NfF0?>sNo-w@7Zm(C(ESD%vlv#&v= z>0PInBdEt`9tf-rU$!LURAQXj17U+Ph`pIAxXt14!e?C)ZTjz5FH?W>>4<~1wMK{eGVfRgf zP05!ON2TitD#2W_w`ZpB4I&S};HxP6%{eHb9El_UZAnNM+MRZvC00#H7t;O9t>iE; z)=VH9rWGk@T6Y~jZ#g1EKUy>wi8e5e;gvWtWEz~Fe#1Xm$Weq=iOtK=uJQ(vQ5 zyZQb!1mz_Bc$?~OnE9AEy7VLX9bfuWcTcH9M(WU&EketI}Z z*l_i-GnbpXobDWQ4S8>`^>sbCoY|23tmnv8m(kvC=4n23b3yd>DAvT+nY9`18hm-O zg0<@GWoB+o&O7f&=yd(;7YqDL44uu(Pw#GI%gLCRmiiS`SXR(n1{%o41RTd7j}rHaG8g zO?C!O{z^-AtW&#VILO!Qx?ujJS^24(!rUM!u@ptA?YMSqPNCertla3}$LZa@i(Hqg z&90hU+Ii99RV|!!A=;q~AsK`o`Mn!`3UT)f1cZlNkMqW2lCkqD;|RPNb;)kOFqk_1 zj1Yz5=9dDJx)u%^oq3eOkls?feZX`V^Ee)C{`72il|&@pmiJHmchS$|A1|*TAq1jf z3lls2(N!q-wMJnJ8xuR71-Rd?557_MDO9$3zL;J;EaSdqRC==A(axC0+oYWg#>z84 ziH_;oRS?+ebh(c$E^9s8PZQ41jQ1@^B-i=97^Y#C}q$L>?Q1oCnKMUda? z%dGg`=7(VXp&D@W&dg>Ec^|usYSv;ivxRYAk`9Ish6_)Qh_Z}T95vlI(}c}uV5fw4 ze|Crb@aU46hfIs?S{F?OaBidW^ktEfG$`E zHx%HvZ1|pp@)^ubj!O~f8H0Ov&gjWpUG}}mziGx8*dZuDoL_Y--|oB-c><&PeeImG z?F4y)FGW3PAdVI8N$$Vq#&p;dMQ(vPM&>tOH}zewyWDyn=djdB#r?pG@Io!k_BmYb zo+)hr2Yqql{f27;sY{oiQ~_3L<&vp*bCL37JxDz`6`iSmLHKl3y#wEa}S?dF2d zEKUqV_{2WQm6d)~Lg52F((Ub+c){-jno}1o# z^I$pY*~0P=YLj8Zt8=;KDr&q|Ory$7$zg2R4rSb{RY?{}D~K?(1{SJR zH>eBjXTg^7z5}J>npq+f_LdQ{R;qr^x*sUzZHnXP?GE%dRC@TocI?DSQTv$8CPcMV z2~B30Dix%LO3`3DmGhP;Hx{I-H&8N&(h~r0ZNoWOkX8_sg^=(t!*9uD^S5nt-jZ8A znN-6*JePFJ?iX27BnA@$QdgNHlM*Z?Ega{+1}QuP?emGkemW3ZGr@+(9YX-0n$F)Z z112Kyfge?~aVAl$FhVoPX~IcMwfXr+!^8f&je0r@K2JI#ma?O>2CcG%)1H8MK@?xF z+fr)!hxB-F)*#Jld8~!m`}O<5DbV${w;91V!8n!lZ$LX?I!7AwHe42H?$LAW`Yrmg zI`v(-;(ZF#x;cu%8Xa3kYG86E&E%y;bBGfWYakUEr(+mvHS*h5YhwAtTr85!2)M6q zHfzk0f_&OykP0(pp+SmEQQVRXvdN%3rm}RnX!OCn>1Su<-4cuR__BM+Bmv5SIT*~< zhd)eVI4Tgt^)fSolxTGgBj2o%3PBwIB8z$f+ z+^bbF4ev6Ghk@s30>(5DG=JZMH!(M^{^FEKPD=8`CPkx9HYuIMv~p-?>Ax8VuXbdA z7-C1obZno~CJVSBJ13nvCc zgA~0^WE1-Cmu7ge-7_bz$Zz8YN7_kxr!H5@rCqGfFfNtkub}I%;=X~Mus_8nZd$f; z2eN;@4gw}U#B{tJ_bEY_Kf0^Qtat9UE~K4Mle=7a`3#HkJSI^mc=PCi#!$p~Jxc9P zmOh@x4;}Roo%vi`oYtNQ;2yZ`5sf#$EV@8-$Z=T0wE&qQsir1X_p|BLQfWt(Q>5a& zR`Vz{Gi%w`NQ<4rB8-|u`}M*qea5&_oJduDCBmxu^L~tSQl$LmG_9~6++2SN3UaKl z)|KdOnv=|zDVAcOIys@VeC4BT-7I1wN&oXoQQnXyeM6Pp>x}71DQg3;h?0Z}I-~6P zZK#5tPpKP*m7qL1!%UvSWTEE6snqpnX50vYRoRq>5D%Z1D}9m0mAhX6k*Xt>-b)<{TaE4)J6}9?fNyoXd{ll5v(?Y6MMbObcle^Nx z#OChua(#XC4PE5{YBq_c;!!N>MpZ4Ik^SQ_+jEL%y+TU5c2FzPgY@VZm!w#Z29J}q zP#E#VNGZJVrNQ!&57bRK;);SGe_{9U-QryuGZl!Y`g2PIfoHQNh+%tE#yhy)!q2lp zeC3^>`+#l*uB_9s5ZMi!mZhb)<06dgwO=jt8Is)BokGs4TtJz27PsvvJe`;rRb6jN zU*K19_)Qs^zIJa4YW--rjBv+eJP%Y1-^Gi=3bP~#3o{PCuXz^deDvq(tqN*;k+Zub zK7n@Ax@v(V0#-{^$ZgjPsRXIE16*dK(AQ`EHv7~FlCp&H0eOnm+eI<I(}~mx6XXFxwKEz!b(ESfLo;zRRJrW zN=-9OlT>{*NlP1fsiREPW{9o<^5maoqUMSPUF)bo7_3j+LKVqm+fo2$LA#Y!s*m=+X}mK|PH~vliDX9;KRG9WUsX zW?CoQ*Q=>bG<*!(CgEJXmUrjMpp%~~K9(S4f`ug$$^G4)&yv%xo5~OD6>tw;VbsSj zQiDU6XPWxLUiT}|W`;k}hnScwfrCCGdjZm$OhV1fxukgOk@m*mOF6UUSJBl5^h(L_ zy?Rw+1+SRVl7)0s>)MUv}xbM9!wNbn8ZWDcoqS zEPyNKN6c5`O?a2Y5O4}U{e-Nk8Mnq4jCGUM(y!Hz)*5N8`!qiq^@Td@ez&|T(yag` zeVB&o=4R{6m!edKa09>Yey<-9p0YQ{;85w%HB*&3!I#lAxbwHE9r(g)^U|3cS033SLQWJ*)rEj%S11ygBv4Q_UA zit0V;WLWXQmDdOPP+}ZuOvhbSPSu*QK0ZFNn(hV$&m=o_(h86&nWGWrw)7_oM|2Nn zs3aVX43}S2(`YSh&!kpTrAZdcr_C#7Y?zKv*qbgLm=qk%(-3=+{|@f_V*nkFMDZP9 zq6kQ@Zvw+oI4;ya=X1Q^-idOYuuq*#-T5&j>zOoq0%vSQ5a?>Ezf)+3e}_ z-Gs>=xhyi-a`yo$LsV4m%8XK@W5pP@S1|C{NgmJ z25Bq6-#D;m&y24Dq7F1Y*s#REYS9A_-;shE=T#*0>ae3su~=~6W*^k#bf#LQ9Ukys zeTdM%5}l)tc6|Q}0WbFP1#Hz~6XM?Y>}D4uyfiZ*jt>gfc=V5$UPZC-s~Zogiq;=| zv&kG`3yXf1lAt&->N^J@^ z4kT(qF@;Sb?jzJEG(6Hn1zSKoUFC!`s>U(v#elga(s5+odmQQ?poA4 z^tiyrDnGfra;d2$r}ywG^e~Y_P|0Y_f<8!d09J!aKPpxZuCnHUxG&FDcfEH)U!WA@ zp8X_mPgfohUaxhL=p@-q~^Af&m)A?t=7ju%wzcZ-U$y*%Q z>UZP8@pTA#8jMaYOYIT(-Q6zE0U1RklueUDz!$O%nDs=>yu>kM)$&}2rE)IT?zZA0 zm%BLGmmRZPPO-6`X}vEzUJqv3Tz=Z**^*?C z9X;1;w#0VNqaXB-!Rb@sX{B_;_*<22)hn?ymoGJW&mobX8|p*&=#!a{bv_a((C;@YwNa(+v*Od3|23<++fcqLmHoVk`cA8V5lrD@c)CXF}0 zGt`(mhd-^TNXknxU>^@nqR*~w$u7{$9Sk48cb(5XL*;Val}GI2}QoMyL0jT-#U0!HXJ1C+Y9_Qvv8qku((&@u6Ke zR}uB;-YLmPrQX@4L2O=0)>AymMyvWhQ>2oTLPD7^cMc0ByP9u#O=@4q?x=g@Z>86% za2b<0UtDTl$}RizNX{yEn4o0I?@*K(wS*nhi(7o4y>>k+3e}WC8%0ux%%$mZEPqi(v(x#QzZB%F1u<|neAXm4f^V)~$oxM)k zNP2gFhCiqC;rk}7)~ko@|8)fIxsU_nucnX4)P`>uksdw!9xbE|Q~L(sW0}IuB03X7 znz4GqML#%!yvP*OG9_j0v79+lnQkQ~c4_+o>JFJ(B0fXnZLv+#PD3pz)`lUKiL057 zHM-G*PHJCKG`~%I(Nsc;Nm7GSifx32Doq+~sE9ebQQlpZx;0Tlaju(iMt44W0acPg z(=M+lUW>X_U&AI{7v{9w?KkhqaA%DPUP2i!AQdkdp4 z{_U7rPZ-r^?8rp#vvzeZqfy;Z=TargrwoGPFW9|t!_+;NESuh)@t)M*E*?$k5F?#8;NK9NugW`tvUh3WTA&N5WxNif$) zXl`5zT1F~H8fnC-cm@7CR9C_R`G~2y=yU89N7~4M1f82mz8F4F*t3+Bo{H1zJNB{9GtvTeQu zmTKfSK;>zZv?mRAt1I^EyWQO5;qazA&SHd>?HD)YNdmHp)$HJ-ea(di-uP*vZKupx zR{8HWCf`^W&i=tM!tA?TQa`~E(Od*m_G<|24y6DdY_H#aS{#{!22pc9y|%b4ilXF4 zPFehLS<5FDPNRD`2eg1AknWi0J0w57FU0q#mX7zO2EbP7c(AUib+k_(|I8Re(e=HA zW^TP@;|(Vyf(f>xWWpe7bui|XsKWABa`kcnBjuX=xrV@mX*Gg4!F6d_{(Diy_+j-f zmxpE=sf^*1(B0io5fxFodY!*Uc&~zgiiPv1zwUir8Wi+5XZZ9@9`+1i7-ag*h|ntR zIzdvHnXx5fw=c|C31R7mXmn7(nZm;K+iT1Us&3Pu;5v(%qB6$n+WF$#iPES;^v$yg zZQA=s&1W}qkNjzzcD?UiwqZf}xYb4{nZ?}e>HpwY;438QouCr~CiixeFCjujf$jDeYZN-E z#?C(#%vuH28zz-3Ym`co_$wbRxnU`l1ij)P7*$fBJdQ(xh4ia?ptfpLfJ!t$ZmP*4 zvOK%LhONc8aU!CJo2D~@K5vDdMZ>)`>`$c6lfmGQ5@T9*QV~=Rb?M}J^H)s8wG?(g ztHj@b4WWXX9~n5IG|>pl)UKpBr7pjuVnC|Chhl;k;b`q~I$dIFmDW9;WN^nQFj+KB zpF8&n(KMSU!)$01F7GU%G%B7=H@tb_=I!11xow*Y$U{~B?FHtT*|<$H%3anNqrPW8 z^uoj4q;C~(Z0J#d1-vAy}r-=UhGvU$qo^P~e6sfay1KeQzGTzFLj`EmqU7xsD{ zKqs?Exg_0V5j`ED^z^ow|4OTdO`pjF4u)xg=PxW`19NQNqV~I6MahiylS&iH52nxd zH@|9eof5?pH7vh{`(w{@oav=zk4lP{q!ClX))rl(WeaK#7d(Ztpv5`WT3q5%%3x(>tFZYM zWezyK1wN#a#k)St5U2=nXDxPgReALN+ya?gacyT%Ee*=K%sP?Vw0UYuK8bT$XdqIRkWDNJsKQ9yxRiH;?4CKD$7++NQ ztMjS-19SMPCkmr;O>xQ!z1FJ zg~tN5(Vr@cyU7Wd_ps8D7ap9N2f6zVW6DatViNC1+~D91oC7QF{_e@&2NJnLN|2Fz zLRAxTqvXgN^Zm#iJL6l~gX!k4Xy25>JPx?HFk7`A)`>0~16Z*q`h7Xdhh8ud-Y*gj zcelpRIr{;%12I(gUiV%QdLG{JO9Vv7h{0pFCzY>gkRRa5{`j0b!c-emt$up8Lw)!n z>9wcy)5zb%UZ~#C0R>znTNhtZFD@n(+AOi*+Q*&D;NDuy%^?0!;@`_kis+wlwWAZi{-SgAJU?I?BVZ(a6MB9$ogLP06U}7P}CeGpz;DtIv(EG3U!pFr9 z!hE4GRN0B4LR0wf`9Iaf^8yuss^9YRIs|ESS$hTfc!Zm8r=0mb^}(oX$~co1hiiG% z6kWbCKuWE@;pW=K3M21Q8(u+^3cbHUd)zIbEuSCMXhYo=e3!*HG%!oy?;mnSU7kMo z)6D^YI#CQCsL1kKE(OauIq%D4`D+H|fhZtK2|hPlKuMif@BvXM*t6X;n@jz5)>>*+ zpOTz;6WP-7mn`43GjGUWuApq3pv}Dg4GpT4pF`)wL4faOC|cVnKjVU*VKjV=YHh4@ z^=gE%;dgEQMb@PxBe*gqF7cj`lsa(>yCffw~~*fHnq{Yq;C>`jBK8AF1=VvdTlIkG-IEoryb#?3C;;7%@;%QqRsLj4!CrTtD3*8deyTBjV++e}R zey4-E@LmB=}|+0X0kxE)?__<6ylKVNI(z`I&-1-lc)8nW_B|1b@QrMG~2fY5DQUQuLCHvZMykENwC{E zaAO>N9+!dAvP;hN^j_WUSe^FCibZ2sv)akuvC+ck4+sc}`bgYm>g~ujtAeVu)YQjX zh-*y*?|2|$;=n=%y2A8v!oT==yMpZ3xQ9`se$C$Ox`&993T%GXW7lhuM&F_T{wy|Z zyyF9|`Yq>Cg;(rwhj{l^coUzkP=I7N7#=n{88&Fve7kIK5$(}#y|({LUgq!=xnm64 zHR3DU7m^^npYeu90}7r1h^?cW{xr1RSt9u2VfHR_v$YiGuZXsLhHtD$-Sl@gzEY)3 zlgR1W*3ka$(?nOpGCfM1R{oC zqx^bJ(G)9iK;jSv4Pqcd)C!4`6qeup>=FIe&VNpH9|oIJw4iR6-L69*#XfOPS@TFl z9klNWWsM?cBefkgi2jBy#LAV5D9X7~9Ou#a6{?2(nj6^J0UY2?8>XMvLp~)x)?Ce} zq>Be^*!!4dxpeuWF4a$rMGyuC2AAx~ZZGnW#C#)q?a^S3z!XG9DSGe7&0QM-XIk@Z zxBf#Dygd2&+?okrPSjymWUW)FshAwCGDeB&AYkcel;g)4)5_wwpAYsZB~MRJv&_ry;AVsHiym z@?j_!F8zCOlK*{Rb@0BYFcJW*(pHp5ZDlC!^>XdaXg{!!weUltEPQH&Gkvpjj zyt0CL8G2dEU9ZPhGmZckS+KwAKAsk7k46pwLOfLSkdNA+HFgu}nIjW3Yxa5qMLq6V zW!PO9A<(4bEg9fwL=xMXM2|`eUzJ==fS;0GnWloGl$df#yDuf8fYk?pic}aJPmR}2 zMx#ZB;eb^9RZp0vC%V_`Nk)_Jz`a94^6i^=QqUN3`9)s}T}sgCHGXA)cOvyc7BO;I z%lq$sH12LR0iapD7eO?$b_i%mE=x^!1W0pyblW%dN7 z|HIi?0LRg5dp=HLro_w)F*A&r9WyhInQ6?-cFY*F9Wyh=%*@Qp%na)<7v8;Z>uv43 zRb5jWjn2`()skvTQvVLV3f6a#!Fet6h7Q6^{?xKD)vE7|^vi7o3}Vm7`9tJCsiOm{ z#zK0CZVyKxqGd@8-UHiE3EaGNfPIxq3z`pk%>?U6TVT#VPHC3es6kDk6VvJlhriMO zns^^9TH|#t=@lrmTa)+-dEJ`uO0}Hs%d~6WYp8gP0{7O9g$U26e`h~D<=#g zKnumiz$6U4l!^%)_}JovPN7DvtZU%>j-hH1Zr#!>~|LjfR z&g&#gZi#(metv@j^w+Y$@NxQ8Hh)B+uc_$4@vZM&7Hp{5Nch%8TQ|bPr=D9Fn=Ipe z-oIWuMn;u=qxPhe-y!IkiBP zMYBo9L$Yji2E#W+t}#C zTmAjX3;){p1ucaf#ahKQpceDb8J|y*N3vVhxV!}TKi9&54;E|XAn4TP2L^gtds4AZ^#&aKqER; zXYzGh7ZLP%PquY(#)?^R+Ew_^n7+79pEjbrz9NhhP`-xhA_oq&bO_$kbxb#Pnqqaw zHgU_4H+_19g^7`;|=K4PNbG3rkrY{Kb)4!3cTv*gbEUaN8M$5~S68 z9!%g!s<12QhOB`0MPYs_u`25OHv219T`+-*K;ChOc2B7Bpk9`CMFNEz(vME4yyKk_ z^q-7|)_Uw=z}HP?2^7_VI5q!-l4y!*XrJNZ`=v>tC<_vW-Nvwuujy43l1YdsxRPpo zaB(!cg91bZ6%iDnP&x~q@2-n}rU3`_h$z^j-(7=sL|kZpls*^AJC?e3inst@#R0va zEg5F$#lPw$RMouZt+_N_(;cj#*=MwbZIoQ{rF~aFUiWM0Uvj4Y{i~Y`WVA@$Uqzpg zR!4t(dQ+s#^F;}95C%nl6i59mYywRMB(pd-B|08g_I@+_4X(p7?)y6X`^WbHJW}8X z0%(>R7G#=n<1Q9%_oE9PjyYrwj052~fx@rq*FxA~1s^S3+vOx`wvfc)KJ7ZTFNIKG z{hAH~5N_!AdXbMD=J0d4{6xl-4RM&Y3pS9{`Gi4D|MPPr)-OLBlOJNk^Bc0}KFZTe zFwYCAm(v2A0fzL;(HUeVkTDn#HzND|rC22a>5P-k!(lljd6O_L>eS_eO_ua?AsXty zI0W->|68x$hk zPfi>D4oPg24M)WOE}mZcSvTtGihvO>1y9-0bL^a9PoGKA>gJtJkF~V*T3N+`R|DCW~}4 zvqyx4{)I%&x24lvg_lKumj;Zc9O8L1FK`zNs@V$otr@1etcu~9GK+b03-(5n zND;JJ`V$075u7E*65(VC!m3k}+iJ^>iwklZ*(^(QBq@%4GyFzz?3xi;rMn58|GT7lC;m;H242W=&(Z~u=KMDAc4t3c=FFZ?!mQa-vo<%i8;p7m zQl{h(u~*G^Bttv6@ykm&&Y9BqxR}3KkzT{@`#7nFO-vD>^6}H?!}Xwj)9C~b$LJ=| zT_6%}iOn%N$V;WZ>>*{;fh4x{c;0RiPc6It^9mq~P3M!A`v)b6dDhnau%>C@z8zHry6emIMnPU;ZDg;X2$qdeLv`e26CARBlGSkp zRHmk#1?;FEO15#$(Rt_5@AT-7Xlq>c(}S6Tc}6VFS%6B)>XFUa_u#X5^lNAQiNGtW zfYQj4A|KYAOe2hqeF=S=h_l){a{7s-G@3?GwqJ^u;oUW)6P1A~9nKB{jzPmz3b_Eb zC3(=?`0vH`W4p$dCo4|+AeB`Y^gwErlQ5i=A=>0Rwi9sIK(w? z$zmZUed8efDNjS-Ej%+g(`tZ8MJj^K#Fv$(@zsZ8nTh}fK`d&{N$j3&S6t*`b)!9C zXLIA}!MA3%O5mD-b91$2rsfm~A7)nCW+Hmjux%R)(F+5AqNNWMcFNgAdN*i=uyU7f z+3rSCl(3A@+BgQaA~)571h7byWWOykkvS|l(&Fm&b;^b~wx*$_q8@P|-W47zQ7o0# zXxv3w3^Wbvv1AvAlg7ibU71RFZBW)yaaI?fi_goKaWc<>Kbp9=UYw}Js@ZH|EyZks zh2(G-W^TG+{3>FiHH+}1b;*$S;Gp`kFC<}(ICQG1lA>XGu5jFc4yuoGt5L(D1leOJ zF1^}Q4DY~7O*8)@lbHbYLj$o`jUIYy@673*ypeC;NmI;nogR(#i*j|I4Z?}UYmET3Y zH|z)vkq8h=*t--WQc-^ybzB``O?a4{Is1e9;jf56Qg+zMYf2!RN3RltInjtIO--yc zw3}USCkk$=5}y?Oy+o2W>AEt|J=jaA4I_an%^i&?m9jG8i;;iz2X@LnmT7g!wHf*{ zh$$7lBvUJ@O1PeJnIU<90AHK}rrLq+citOG#Hz2G;rRX`f9BTO(altO{0{f5nwVk!7`BYPp9w2ud*CFW)+-> zZN2Wksq>J6XxfvDJ6RU7z#Ei9EdyL*F*S-Raasx^y3sudRHYtXXnz% zBJr@~%-jYVD%v>OQmdpQCe#tH9KDnEN8qMfKOB7Ti)`gvk&S{!;0+qNwD^@})r%L5 ztg2$cjf#6LX67lXDdH8e;Bu19tSXnj*RJN(OHUlhw}{__D|=w88BU~_Ez!7EX--*L z)y6`Am~u&LjU<{-M+m+exjp;~!D4p2%X5bJZ|& zDQ4Bym-v9!k5s&{j2u4!C*V_p6$?*NDKBLZ+xs1Jae#GjRbWva`vig;nq7)xa2hVe zC)2Uzt5LE&U#HnePX)2~G>b{|BII{=(-PzG0Cf zOyWG~g`2xYAR~)d#-XfrYB;l*T<{~w(0Qaz&bNNG%7%>ZR7H}dgRvUpZ6_m>K+;PrVaSioVa$eedtHDPgKlf-?#)%fDr=DQfQ}h`{{b zQ9EPhllp6lm|-Fjt)*j9Av-F!J#>Ggp8(}{9<3<4D%)a536`2IDSD0QC#Dj4Y&aJ7 zxfv_qDzFRcB8+bT{gA^n$zax;T(V+yEdYLIQg_>#tc%3krBXxJTFB&4`iDnumHu$B z9rGr;{FM+>zrW%qZA)+FpqUjWQzE6??lDx9%5XQ zt8-pZy((~EsSlZ6lp{64g!3AJV9YgrylFKM)-0EsNS zM4Tnlnp-(I7xi;h?YcMkba?4EXUl@<3#2G|_|NqRa#hS4-#F@5qLnz-eMoSb!}1uA zEoqFlvd@knuy44DRJMo;P^0S?I+DK8M}y!SQa;Ky$D>8FDD~75#~%{OD+wwW z7auAG2nQIXRzV!v4s3r{vNToFqJbu!9vobRs47ZSRA!AbEE?=Is4`KkZ7j$OERl~b z3OTe>`dU*+2|IWMqZCiNO`oWu2436@L-aLjRyjJDz)xIB(Jx8~r4Uv;vj``TQe}pf zB(|q$QQW^8BZ_jsEVofvNmivyF?Pn`@USSEh(HI*gi0yC$LyE9qMV`{Y^V-tO|1j0!JKGxr^=W;ib50zqXbv^`* zo0#Rhi(c=YRs@U=*?M@V+MR3iwTpcCsN2aic!myx#K2Q z@(ijp_1Rv`vl$Snhaugw^1ABdj3|n^+qLYqNr#)1oOSEgxe4Y6$6>fnhu|a&d z8A>)xZFm6CWDOPVC%@v3xu>QrCJq|eESnr;xim=lS+Pfy7x2}OUU6gDq_+vR<|hI_ zfy%W&KdH>#p2*k|i!jJs8(=&i5~Fbv(utmN4|qMk{xs&P-|FzXqlCNp;}YSN#RYP2 zm=YnS$4G(^%D4E{;wE%YrR~lF`E?=yE7Oof_lM6IB&y!;;IYE3OVZ;;tN@|YAWDHB zec!`N0~lGBM#uunm$6x3NIuqT8#AZahd{Ytm|aADzJB=f{VQFh@C%Muj(MB3%&hFl z{VCAFCq@#IaOW#yxqSeaN_rVaRC;MYCj5+j0cB#*@@xKi-!diF~4H&%!}D?j!PnXD+LvugG8i=V$Iyk zbhf`)AW~(%%~RV$KXCU6J;=FePlxkC!|-Jdv?k~9?BOE4yqVX5_~Ox>!g*S)gxhjG zB)hdm=lV138wLJz&uHHo+^RC~;9YA2gW`GQq9~DzA6%MB>&wn{=mFDg#=_0#!X?E?i(i z#E7EdtIK?4_V;<~!d^%7y83J$%pz{CX?NJkXSJ8=(neijrNJFFm^{GTi z@;c^qICl&#j8rEsyHyky=@2eXREPmlPLg+Qar~v-gN>-xXga{3aAJ0L79G0frmW$_ zPNuuszo)UsaOj8j#Ss50`_85Z@iDgL8TXr=k(}Y-e(7K}Du~Py`?rzVk>QyC94~hB zw()A84vkfmwijD~Y}MWd>FO z>5=@+*;Z?_sUDB>?eKZ1{<<||r{uS@-o*gK@BG8~aG47s@=vr>tL!{wue%{74>~*` zkMq|FTM6sYV6hwg!n?O9jT;RX7rU3jy5DOHzh^ji&$JtU$ycsx9Qcuvd-CEtcQJZi zZ}gj1{buocvW2;ry<$7XecGuA(**3AFaaG$sLDCeJQ$Lb+F*!oIc{vpxTD-F%@CP{W(fZx9_e=INFBB7| zc-fB9j`PMI6{^G7vTKb>lBf5XXbBms%BR8>f~=7jaU85Be*!V(kb$< zw9&Li1ZLW}=-#T~?!)WL8e3eRPGQrCvfOuKqTUP3b}L<+Z)D9;$u-(ZDA$)DLo%g@oDUu3qLC-bxC|}> z9VZGybuHhX_x;q!$p?OJ)9|2hUSB3?B@KzBL*eH)UdZbm{=h6}gL~Pjv=l>JxFfg3 zRdwG8(wpI|%3JgRS_ht$nd6wgACj=e)m~1pTm)8yx}X490v}xST+zH+%a54ZS(aM$F4& zH&b`-Geyb~<^~uinY{=lL7W{!@Ps>_u0UT` zW#{m6jY*I?Jcv{Hthq2wJaFwNsH2_mmWsF->2wX3eQQ;vC{lC{rF4G!qMI(M9w|0M z_0a1gG%d)9cGZm#)YUho1Hk5QcAy@5-s~wnkyxUw;%#<4$o7l>nwh-k7=DU=*_C~S zEsDYdO9zLGQhAU50JpHZ{9K#gTrWbZsd+_xV{*NTcs$hVT9>n~K9X1Ou{}LqrvDAoZJ0*Iy8X+zGvo(Si)JjPF?s6zBYDKD&3V^@ zU$D)Sj_a`drLF2Iw>h7yf~ncvdOe;-My3T*D=%3erlG>|Fpfdgd=Y(A)pm2f&(Bhg z7vWL#MB*^!hqZMaH8<o?g%3HSwuUB^2ePOr?i~gACX7N=UHW|ku%Z@e{Ak^fMO7$>Z78NLLdpPisKpaLJ;K|?%U)|2Pq5ZsLr!$ z31+IZncQV&>V)8B5QVbnBaG7wZwr&rGSqFzkCQ%wzOm+NgVy!e*H~Dcb(!bOF7Aw5 zx66y!I}}{7xMpHL#1t<;!--C6*;&Jk6M(-YxfL@Pw>MfCCm{dmD>(Z>*$>oJySdd5 zF6wteWkksm+NGhwNp9>$pJi z_Q`K2W3B1%DQKI{J9#H{iKn$6Z8=qiPQuO%gZirsZ)P2i^3aA`s;q3L!du?%RV#GW zpBDGfhuvwbw?=|i&mZ+J5|03D+&0_uv0WLQm%^(WfD~WrkWzHKZ>=1lvGHB6X8J9K zo3*KGyw%GbkBrA^kBn1T*ZX)uz0>5KCVv?n?aQu*-u>Ww&sPIk)7gtxLLGBjw9_}; zH&_PzsrI8mXQnY>7HHa#C}l_D#Myh`}lS9DEEto%81E!b|a;uzi4?rf`WSR>t%A+aRLT zdUQ*tDu)T1*4p!~_d?O}zNfa2XBG2dHU%`yn>#?og(aGx6-SMgn|rGDXc6?Lh>4Q{?#1L_Uj6?%4eF zo5E@Q29ZNqrK{7G)@$@qWA+$SoR1$b!+V`CB<0H$!6VVo{IYEqSYQ=!(=S;>QL6Tp zDl#)DaIVf|yqLU}g~dQl%5c?t8OKmuPDI%;ga{HL`(@{4pAYUxL`?$ohJwX0d_rJ$ zyj-#L@SN7)W8Z<@C*z36rC4DB!}`P#5ICn!o9)Z| ziU>;uC8u3pDq#6ij;Ml7oAD2X2j@fEO5vvy`E&dcl&0gztg2M z*MaV6&MGyG;ZnKG7n(_XBC+q9olSK_A2vtKweh~xIE!9vrRjLp_cre@d&Dgjy!2L9CwPhq0`z$w5h$#4{~2wtSMTn=EJ>QFMH3WiayDX2M-%!H=Py+>u77! zalQwrEIr7Yg^bX2rIKF)u@#MAbBbQfM}pt+%Hlj~iwx~!_BOs9SfLz4&K}v4DL>~s zW;?a7JTXmy;r3cEoOMcMZKNK?+(>~(BYCa6?3HI6^ z;%VBi5BbLq#(t_v5v~T8Ndb$~mo?r8N6fL3HudNc;7~UJZb>rPj*`Nxl!k1!M4Q{gEH@-+^SlpPi z3g4i<@_1RU0jJ{vC9#~u$NKJ{@8>NDhCv#YyO?-&Ht(`2;H5KD>MuCUscZsI1byZj z1EqE9IUKFFI|Yj+t23I{S@??*nJil#o~BsxTdmy;FFU7_mgdfW^aHt(FDLetCaFDX z>M3qRhgAAP%Z4d_;?YDh;N?Vc8&Ji?ovFSZ2H8Mmzzvjl*g~O8$>hAf(Y=8O*$6ih z5pz-v*+K=Gkrlo66h34a7Qt_5Th}~{3=q?;#c|ob)E7?2h~28MA)9jrM_{&j-SJ7N zUEbfVwiGO|?CcOqiRpVn75vzuB#O@@l;}eIL2`vZ-j7|^59?7Top@to>9bcDq5=U` zkHA^)JSCRmZeY=OC9Eyl+M><)vOby!eUMFtqJyo($n>%S)s*M5aeq*&fj~#Ab3J-m zyA@$Zl6TKd*fyGa`teTgiwbEdyX{@omjxSPYXE*SXqxWYa$q^S6@aJ>ke@Yeyx)GMIVxXC*w4J*kRdLFs-^J^8S zdiGkXeI81SYmvY}q_mMPV}CLp^NFRcwABM)sJ+Cy14(C?bc4A%m%#hfnZq4B*n(u~QeV_W3C^=&S9*HAR6fe=*2>*7W*8~EO2O%=T6fUBDz#NH-0TjPF33b=JUJGH5Uwq? z3kwhIK5OjePCXK{+*v#gT~_F}MkhS3jo&=J)p4INt)`*y9yvC24Mpx5qwUJRmw${OimlL!qcO-*YB{j# zB7dZsn(Sm@nOJI&S)p`eI+${%b2~>WT6C#$pca@wL#f#_+p|VdM`ina{XmWFe%Y7> zr^UwcA>gid1RqZw-EK9KCMmN!+Sy#tTk~}_5)t#1m(4(japBOd$gL+7pNdP_t4&2Q zAu&y-wd&&9Zj|L`(;5z^N4BZNgxUffr#j{imZyZ1S%9ilNUMV{o4t{eTO~_hHD+VE zD?Y{%w5cceFZ`^s`!H#^Ufn1bVwxNy0Y3vGXk^E}&CV`&VKvq5T3R{jQA8 zv^p`?hZyZ9mE!c$9%*n>C^UO)RG@xJ)ZuzPPy8LGk{dwkAht2E5C0wu{S`xB#iWp0 ziB{c1ZtsD-JP8TsSmsn%WzN)fj{EgyHFt~;n9x^_DiHn_P_MSOmDcB0;Oj85kc%pc zG}6rMq0L!-sdM#Q6#jOKA9F@0={iZKZF4kZR#+GjC^g*N&O<`Zt|WsukI}ObAdIJG zrgzO4C4i^CDU7Mpj=|mdMx5F6aLg%^z?3-$GZfLL=Sh6yoZYZ#DHSmErotO`!D{jx zVDKTf6}Uy^*SiN1E(3>kSM`k^nr5}BLDkx|=IHXquDnKOMylnyjaMY~N-nkU4i#J8 z35$6Y&Hzd|| zk*SSaCDp6I(yqV6W{1cIML?-&+N)8KCQEr?n%s@9UEqp8F|W>N5uszt`>~W96v z2V0T3rgdY;%i-xr$MVBhOtQxVGiZI87mD%~hdX;Rr8XO>wHDUu{FY6B$3!>9aGCq7 z68)n$@)_eFSfFFw`)%6!Bn(q#(lgDGoyo*osEpClJ{7NtxI+`}3a62$9X)Z#IX;-OI1 zP?u};0Q1Ja2-vDr9Nw`P*@Fmqohvm*<+0?eQb6D(X7OsEd!wD#j%i`aQpT*#Pp&r_ zCi2zn&FfPKUhj88Y87LC?*D*DWcmjp@&6*Ez5_A;Mo0aNtjYQg24!XX3po0RcFOV> zWR!v79hb?@{uep(@6^&i$-iMr|6*|d9aH)TNcuO(=pVA^AIRt*;3m`GprL=6G5z6~ zGXLRw{-K2a;iNMEp@jZ{mooo>fHKp+OZf{W%Fg`G_he>$CwTrlf0OyY?&tqvbF%#7 zxBn-b^WE&<@sCH{J`4$4M>DOXzEC$R&N>iCBHj&OtO_`#>y*%6hYun~D`@NS%VlubpmMti6(Y4ir#-38y@dt9(~lQ{*v zy=A;TpMg$WKJk6f#dG|qSaMdOynz1}3g+Dq-8istcAntTWbu(4o!GJ(^o=CG+1V43 z))|8j#r4JGMP;VBkC-d)?vuxAw01DSg}nV)^sCoIDZAzw%f8C562x=uLlJ>azh}d5 zREQT`PG0+1S6HVW-7+6-2+PL~oOgzYG{6{}zb~zvHS~_<9u{z$N9i1@2t;_0YuA2^ z6AxB_V}FdV6g_Q&Cb**ZrdU0L4J2||?{xX~{OY~AW`F+V@Szg*gZr0)a-zGDtfRBy z#h6xsZo@9&d{uvq|ASPU|^4xf&fl za%89a@3b?denM6S5({{t;Rg?X(I(U<@a1n7)$}*g99Uf!YS@zJ9w-Hv1h$Dq6kmbi4^`-IpDo_r>Nb$j^H01D)(`j?FJ?QA;Xk3Dd<}u!M4ywhHN89p%=gmK zo>iBdx55w}Bee&5;9$@TbS^AcAB{GSLX6^t3Vj$&Hm>lmuf2_h%{HiZm@RECaTJ!i zxipkOqbn*ZsZbvr5|;=Gg@L`i+SaU&tEPA3S2hxHRW}pAe9jS4=xn}kP}L9PFGmJX zCzhS1EXGq>`#aAxmMiCvVn5HOQ=ViTi0_?bMTBzQWp zdrB6^hG%XOhmET5f*FRb|20hbyHxDU7K!EixwM2?O{P&FLcCIWrky+^7D1~lvI2L{ z>aN^6%Y8GeLB^Dm9Yf0ZSHrj|`{B(5DD-_q@id^$VE@hq8SxuXdxA$8spj{g! zwlu4TDr+Tna2<B`H>xNLfUzqE@n(ni$xU zvMFmK6A_J#79&Jgy@!oVYpNw!iyhReRwin6xiCeRL?aC5bHaWz?HF$5b(`q+?U9=B zX+B%=$ro+s%P+eX?LTD`*AdIcT z=q~26Ae&g6@InR+35Q<;w7|W^q6%EI$b)XBvGBtan%2T@%xd_|trpZ_L{nR=$(9BH zu*6nHj~*u{Cuy(J=#%pHu!2I&z9qg&(C3a?JI)o5+<@}p_uVCemaBlEglVqD(w@3p z5CU474i9%e|KW2lEl=na0d1&pwQ8J3eiFqbYCg=veZIc%3Ct0^YJ8YLj#5#fn^a9b z_Q)<>!#5m&gi0mQD9I>KU4a8#hXFD}UwipmBm^wDro`UvgxpptV0j&Wi*ca|xGZw( zXmQG6)ydY>lBN{|=wxwZoA0}_DLZQL7bVZOn^FuBL-9lN1$Rqiswgbkauzz%tNut# zzl=%NZW2g=1!kNWIw=_zr~(LcBAX!x1JCmq-GRs=tL2k07JP@6 z)R2}$))WtkYbbgw{VcN;xLt}avU6kDOw$4)W#(|JU4P+5@E5vWPR|W@g z>ck8coM^$(F6XttlAPPAX~zis%<86&(5bPpSTv^BQndEd?_W)Q2YK0oa*>1Lv1YL; zITw#<35ly>`A0i>w(|?i3oNZudCa(F^)qZk5oAxcCZyb3q)&SVO%CEKw%-W3(Iwzw z@hGeO%`8^Dpu?XI?5SoNGR&4`Sn3wllX{I1rF1ps8o;oQ8x|Iv{X<#wXdA1CZvz!$ zy+|d7VL*>JYP?9^Tric+w>=vmW;|;SC2bvTnfwaf{b{bp;F-M1avk94<5w5O6h=aT zoR#-v$0TeSK?_04!jzD8&9?J4N&S(HytGnb3rctKFL_N^fHU3tnLo96f6AhV>m(k4 z`1FH|o!jXp*|%O_54_P%P|oMYb%Sjs8v%4SxSj8BWH?=Cb-aAY zo@{neVrAeIcKFAB9~-{n`I`|k*`dsKx&?mz9>7&V$TQ9l;|W@k(GD8Q1UD>cSDDlx z^nQWJ@sj+x1qVi~8L)Wf@3sNBN53aF_$36JO5}QJyWKYqg=^9!&5q#p1-pAmm&YCU z{a?!l`8j^K03Mmw<{J(G%LQ=qaO#Ixxtwa1NTJSiWIY2$K6tBfJ;EOvxK&Hb5$k9&E*@eD=kL`#4}}BGhdi%o%Bw*r8iWo6u`_^S9PAbfwf<*4{=yGiL*F)Ok)^0%&dyK>vT z2N(B`I=xE|RG_S~h@2d-!?c2rCb^Rp=u?U9}akrAv{P zaM?~!DIphJJ}2P!+|A)9xXy???Pq3RJ(y#VEKui_zhM9NwIEsgjt?F4Y1 zhBq3ttY<}`GE%y?_?u^RETDlwdZ+g{^0Mh^+r%JNYZBt++B)LJ*cI=d*T5S>@heOR zp|d~#hBQB_Cvht3D0D1QFe7W|{ulgIXOk*JJBb{EDxyi`-`tFWC){Bd0@ryTU?wtH zhl`M1EA5`=TLUdW>(@haH^QFw%}?xe!vmcmimzRU&m_(%pAlZ3xSPT8yT~zc4s~D{ zV(sLAT(pFh^wwcsF$ydr-{_$*!UbRcx5kwf6uuBJdS_K&=Nw(PDO>8^<{e+Wf}L&$EHxgMAC)BtsB6Ho&uw3i>Ttp;zXo z{tbU$oR9Z15R1ygYyGpPg37HTiC+LN67BuLi=8(efEO!$MSgIL-ci>8RH!p z2UuoGpLLgW%h_D&PXCZj-(%Fj_KoEW%yf2fH7!2SJZb!~s5~WFdnAcmE33jOCvi3y zBK%{w+Z~kkTgF?4^mRL*_3Nk~NotPHjdNC~hI4u^KL<4@1c_%W9uo@L9B;h3X~+m% ztc6CZt*v`*h4X72oS}|u;X(Eka=who7XCTC=?zs^_o6J@TV% zP573=UlMb=r3L7}T_s#RJbve)9tNbnj+0sswMT+YdJaVJ0(v*8VyT z4@0ho+K0Z+tVi7gAyhllKM(`f^XD`Z6k;;kRAJ5qGjE@-%Aw&N?Cq1A&z?&sLOf&F zBFMipQ2X&Ob(SYYZBBwaoBaIM+vBeMoOKyPjmKU|#3myRgRQkTNpdMA({3r*`&j%O#kd@)ONTj+#7oL#qIW zjoq9Yho4oiuNpytw=Ikhebq&K_)bTRI_!?e-D775_m0ANp~KshTmYrz1L5?NK}iwY#9{2UNNjxb?O{{<8GEc3 z(Yd6vAJ-pDa4O?zsF}&ox5>Bn(1z*yBdG?1RI|Bc2FYP#E2+XphRBVO9RXqs58g8Z z;u2D|C014qV6HIs7u`K ziqKjIV>e`;l7Mqsw|s7BY%P2-D;1`x46=~j>0S-3J!e> zO-e`4EzOsrzEZuYM&LZHRe(I@aX38vl;7~U?+jz8WNHbu`l7*XclMTRE1WUq`TTwY z`n46yZ9{b!Uh3_z5jD>pEb}1P8??INfw3d3podKqMysA6H8){qiqWWY_+q{cl3;DUEIX( z4ATlQm~OABbqtd)7g5cl4_9^vXYN z4PoN2h4MspUL-Kg3!Oyj^sPTYk@tw|$8d%62J{fZ%da_7L+oV_?Wra?*1YNbNeU;>&!0X=$jx6k$Mz*AbsoU=iNA1as{C`VvZ1Qk-3=#1&J39F0wDm`Q>K3dUOWOUVfzN2 z3M`+vAWp0$y|u#|iS&-$7cYMsAIByTB0D|M-9x;;^m7hSrk@5N`(v({eIc$~<(0C zEf^&%xi3?ha4Kt>MvGEC4jScdsHCS|Ni^yVUCqmeimg!%l5ZKJUoYnV9qC+V|a z61F3%XV~Wv--Xll#178*HH_xvI``Cu=Czo+RNoguf@Vg-J~q0m&%&Vj5FwgDQ@q&c zgL4(k>|o=aKjM`Ao&X661{nT0y;t>kgb;k~h}xDfNQvt!b$$U6n?)Z&iZDASe?bYp zuyu3gGP&AAhv_L%KUeF`M0Nk-?fLxedNXJbN{^dfVDqA*2KVCbB0cHM`%A>9^-iAm zh>>`nsa`*MeeP^uMW46Ee;mCuRH5&sv5VV$Ig@@?%sc0Z6Uq%>riI2zvfw?!{eDM} z08OE-Y0Y2oV**!4U7lO~a&tZ+jz<&Mm!Jy;2Xz~_bJZcb8SC2qv&ShvmX=o6{l^9) zaVt{wdN|52l$|qpHLTuD{LOB1;tW&ckbVuxB4$(I#R=<&FmVhTLmbzqdTm)>Z!$|h zJXtsqzp81%gre_Yi%5NH5b?b+k)f-W0V=C>*)^Nua8o0-NGd}S=0gDH7YSky`HxT_ z>NnYkiT3ns`C3%N8&NDc=grL0f=chIP$xK|zg%sIAM);G%F*e&a#QnwNxh;tA#P>f zPEukB6(JiUj1HIv_Xu`OjBJY!>x77v$fr`LYo~%n)rFae8J4n-9$Av4jVilqZcWo= z)C6nc=yB!nc&q2f7^Sur0?i9}*oLb74nmERXUjfgv$Sd}Cm?kRNSZTqW>}8If%E4$ zC!*Tdse}&G$JXnkWe8St9(wRD@lPBJS4-%VnKl+oCpD%N`SMG}1PN2IJLQiRp&X{9 zxNP)!0DD-6lfg+xOC>^$LgO#)vT3Pq3Ab(OhrXftTY^d|C9gbdIZVJ^S+os*{tUgN zz43^8aXvJR&N$&5DN5_1Om6y$16hj`L|d7kCFfK{=~S`!tM*LK-P4gZjDAoN?WOBCx47I>`HXO+5znYk(W+(93O})qs*$MwmT#R%{HzAiaLU=_F|~!A!!xkS z2EO!`@f~g>hBc$XTDm8Im=(`5yt!@AbR*AzJ%b|^NT@MVhaMl$;{yR~ut z2S>ZKa-Xp)Rw;L1YEek~PoB~Y0I#W{;ITa{!^5CGYyNh_!s~^XRk-^6l_THKaM*Pr z?&d_6SM@2+-+3a-+M`mW~s00Sak9xi7NXGRyn{yK}(K67m(K8V7z;M|a7;(r8iTvH@ z{TnxtiM_oI2OXW0lM}5I6RowKF&zT{0HC91q+?{Hc`rc&aypA7Qlm)|PbdKhQDIGSU6z=snFLYiDiX zpl@i$BW0>@XAQD8vhOS-P_ec%ATYAFBlu_LdjLKbuD^``%YcjSuZsUg$lq`7UvmCk z+TWV#-N$=N{r#5zyENy2)#l70r64A&BMHFr<+2~nmm>JpGY4i*j z7-$Uj^w?S6mtE-@+4XgVt$=zKh6X(Lb`FMr)c)r#{6D2JCD`EI>WRzjeC* z{d4$d9si+5L2D~}Lo0g$Q+p5({eRd>|LOzge?40MK9CLc|2bG}9PBLq%nkk zKjrBD)%t%stp73nS=#(l>AwwYXQ0iyO8P%4-)Bq?!N18=w6?bR|C)6EyA9VrGYo~^ z<6H7D(lavA(7#`7iVXA|OiUb1Y&7)j9Q5yx?Z1`$V<}~AU~1&@|6}_bxz6 z>i^v=mX`l$87V0aA!~hyzn-g@5RZd{sR0L*06>J9i61~CBEldN2Jmr3`P^ zJ0lcB^kQ@B$imIdOxBibb4GtyEook?w6^|ePKwkeq*+5?L*50|E9KnWdXc$7e2nb(1tSD$bfoz zu)7AkYd}3PAm_o^HP~GP>VW|{56-TEbyu(KQ>scQ#XGrf-e0*gz4w0F*cT`h#;Lt_ z=Vj%Tk{4F44X!SKk&ZX>D#NP-ikqOh893F^o4{-RUtfMZfnQV_sK_m=tO?L--$m}i zC5ljVh2C|aTe@aVn0`=|(%j;ziZzP+ASwN|aLLr^j;=XHTD~YODzY5Cz&$GUKPlz? zvj^4x_b*R3M9j58^LlnbKlm!`{?>*Q`b+sbp7DrrjuXY zY%M->tLfyPx})_g-{|~h*43R2PqzK=(9O&*%pTv-_;U$8k1TpC(xGP`n)mp9nY*s! z&wQ`3J0o;@%TlfI*!ISeO2@6Jzbt>_h?KQLtLA5_Nn^S)H^gN= zJmQJU??0V={$kSTmiX4_u-)}mPyM~SHw|474J}IA*y6icKIveC(UNLLYkE@dNsk|u zou02`nQbEyBb6N~T`j|pDZa5v#=5B4k=nGlzA2}u{)m!rFx@|;E#dj}UCD0|q>=v+JDM02`NNxR&bmTLPC>`&a* z6~A|jZ`{_5aS^Q~p}l12%H~;PcAm+aJ?T``vC~F&j$Im=7)B!a6Ip1x4<&%Qyxa*yH*EWyZ*H)HQ z_t@2}ebL0$y6M%a1!~Gq*Pi-4>!a3)rTUh24sUG`)oJHXPn^Hu&Gz$?AH4KZ+r_$? z?26ex`Zk@*e}6_WC-MHSr?*a?extU1NYXnIKb-j0p+JS-r_!vI^he)EGZoV?G(&O! zsqZ7`?#ulsRd;N{q&qH;P3rXYu*c@8^o(q*jV{eteZtn5O{yxZPl7h2bXY7>Gicq# zwkvg;o_zM$48d)t#oM)Xy0`O`bsX9_Vr)XQZ7+w=g+&WL*3Xcpw+&(Fx;JKFn6{Tq z(AA?iX6h=3+oa}gml9Bu+jMsWjEPUG257@#eJWMk^2R4sM`bpl@^*z_V=l?WVi5-C z5SGdOC}=jA+?H8(4!n;LrgF%~KYi0XS$Z zxgX##0EYoM48TF-!P@{F2H-FN2aP$a&jcJM;Gp@(%K{Esm)s9Uxe z7T~Y|2kj-iEa0F$jQar&3vkey=4Ang4LEGTVFL~uaM1qD+O+|P4LE4ui8A92v!jh$E0q@X=E%c5UmGMmYAV#_jVZN=7S*($Bo zSQ|a@KpNn&IV$bD%*Nt#9F5jdjGv&A)c4HhjoJ0{t|Os3bkX1~>zMuTM#7=;%GH68 z-$!?(eRrMmzrNt?ee$V16z;vLqOVSC(((Ipa%Rja^85b0zEb_Z|0B5El2BkBeZk+2 YDc8LME(<@WP)|~?vA7K(?Y_AG0IQ83=>Px# literal 0 HcmV?d00001 diff --git a/data/employee_handbook.pdf b/data/employee_handbook.pdf new file mode 100644 index 0000000000000000000000000000000000000000..878f36f7dd42f13540e6a35905cd45459dad8c49 GIT binary patch literal 142977 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

6B%J@a6P4kSn)+=2k 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 O+Ap}dpk4hZ?*9c*4?!#d literal 0 HcmV?d00001 diff --git a/docs/sample_app_setup.md b/docs/sample_app_setup.md new file mode 100644 index 0000000..ff5455b --- /dev/null +++ b/docs/sample_app_setup.md @@ -0,0 +1,84 @@ +# Setup Sample Application + +This solution includes an optional sample AI chat application that can be instantiaed along with the other resources to showcase a production-ready, end-to-end application running securly on Azure. Application image is pulled from a public registry and the [source code can be found here](https://github.com/microsoft/sample-app-aoai-chatGPT). + +## Pre-Deployment + +### Setup Entra App Registration + +The sample application requires an [application registration in Microsoft Entra](https://learn.microsoft.com/en-us/entra/identity-platform/quickstart-register-app). This is used for authentication. The deployment process will automatically create the application registration by default or an existing applicaiton registration can be used. + +#### Create Application Registration Automatically + +Following the steps below and executing a deployment will automatically create the Application Registration in Microsoft Entra and set the required environment variables. The application registration will then be used for that AZD environment when deploying. The executing user will need sufficient permissions on the tenant to create registrations (like the [Application Developer role](https://learn.microsoft.com/en-us/entra/identity/role-based-access-control/permissions-reference#application-developer)). + +#### Use Existing Application Registration + +In the Azure Portal, either [create a new registration](https://learn.microsoft.com/en-us/entra/identity-platform/quickstart-register-app) or navigate to an existing registration. + +* Note the *Application (client) ID* and *Object ID* displayed on the overview page. +* Navigate to "Certificates & secrets" > "New client secret". +* Enter a description and expiration, then click "Add". +* Copy and securely store the generated client secret value, as it will not be shown again. + +The client ID and client secret are required for authenticating your application with Microsoft Entra. + +Set the following environment variables after establishing an AZD environment: + +```sh +azd env set 'AZURE_AUTH_APP_ID' '' +azd env set 'AZURE_AUTH_CLIENT_ID' '' +azd env set 'AZURE_AUTH_CLIENT_SECRET' '' +``` + +## Deployment + +### Setup Environment Variables + +In order to have the sample application infrastructure deployed, certain parameter requirements must be met. Set specific environment variables listed in the below AZD command block after setting up a new AZD environment and prior to running `azd up` to properly deploy the sample application. + +```sh +azd env set 'AZURE_APP_SAMPLE_ENABLED' 'true' +azd env set 'AZURE_AI_SEARCH_ENABLED' 'true' +azd env set 'AZURE_COSMOS_DB_ENABLED' 'true' +``` + +### AI Models Parameter Requirements + +Also, the `aiModelDeployments` parameter in the [main.parameters.json](/infra/main.parameters.json) must contain two AI model deployments in this specific order (Note: the default values meet these requirements): + +1. Text Embedding model (e.g., `text-embedding-ada-002`, `text-embedding-3-small`, `text-embedding-3-large`) +2. Chat Completion model (e.g., `gpt-4`, `gpt-4o`, `gpt-4o-mini`) + +### Deploy + +Follow the [standard deployment guide](./local_environment_steps.md). + +## Post-Deployment + +1. **Access AI Foundry** + - Connect to your VM jump box using Azure Bastion. + - Once connected, browse to the Azure Portal + - Select the Azure AI Project resource and load the AI Foundry + +2. **Create a Data Source** + - In AI Foundry, select *Data + Indexes*, and click *+New Data* + - For Data Source, select to Upload Files/Folders, then Upload Files + - Give the Data Source a name and click Create + +3. **Create an Index** + - In AI Foundry, select *Data + Indexes*, and click *+New Index* + - Select your Data Source + - Choose the existing Azure Cognitive Search service + - Keep the suggested Index name or supply a different name + - In the Search settings, select the *text-embedding-3-model* model deployment. + - Review and click Create Vector Index. Note this can take a few minutes to complete. + +4. **Update App Service Environment Variable** + - After indexing completes, note the name of your new Index. + - In the Azure Portal, navigate to the Azure App Service and update the relevant Environment Variable in the Configuration with this Index name. + +5. **Launch and Use the Application** + - Navigate to the Azure App Service in the Azure Portal + - Browse application and begin chatting with your data. + diff --git a/infra/main.bicep b/infra/main.bicep index b2615b4..90c9a9a 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -28,7 +28,7 @@ param aiGPTModelDeployment modelDeploymentType param aiDeploymentsLocation string @description('Specifies whether creating an Azure Container Registry.') -param acrEnabled bool = false +param acrEnabled bool @description('Specifies the size of the jump-box Virtual Machine.') param vmSize string = 'Standard_DS4_v2' @@ -54,56 +54,66 @@ param userObjectId string = deployer().objectId param allowedIpAddress string = '' @description('Specifies if Microsoft APIM is deployed.') -param apiManagementEnabled bool = false +param apiManagementEnabled bool @description('Specifies the publisher email for the API Management service. Defaults to admin@[name].com.') param apiManagementPublisherEmail string = 'admin@${name}.com' @description('Specifies whether network isolation is enabled. When true, Foundry and related components will be deployed, network access parameters will be set to Disabled.') -param networkIsolation bool = true +param networkIsolation bool @description('Whether to include Cosmos DB in the deployment.') -param cosmosDbEnabled bool = false +param cosmosDbEnabled bool @description('Optional. List of Cosmos DB databases to deploy.') param cosmosDatabases sqlDatabaseType[] = [] @description('Whether to include SQL Server in the deployment.') -param sqlServerEnabled bool = false +param sqlServerEnabled bool @description('Optional. List of SQL Server databases to deploy.') param sqlServerDatabases databasePropertyType[] = [] @description('Whether to include Azure AI Search in the deployment.') -param searchEnabled bool = false +param searchEnabled bool @description('Whether to include Azure AI Content Safety in the deployment.') -param contentSafetyEnabled bool = false +param contentSafetyEnabled bool @description('Whether to include Azure AI Vision in the deployment.') -param visionEnabled bool = false +param visionEnabled bool @description('Whether to include Azure AI Language in the deployment.') -param languageEnabled bool = false +param languageEnabled bool @description('Whether to include Azure AI Speech in the deployment.') -param speechEnabled bool = false +param speechEnabled bool @description('Whether to include Azure AI Translator in the deployment.') -param translatorEnabled bool = false +param translatorEnabled bool @description('Whether to include Azure Document Intelligence in the deployment.') -param documentIntelligenceEnabled bool = false +param documentIntelligenceEnabled bool @description('Optional. A collection of rules governing the accessibility from specific network locations.') -param networkAcls object ={ - defaultAction: 'Deny' +param networkAcls object = { + defaultAction: networkIsolation ? 'Deny' : 'Allow' bypass: 'AzureServices' // ✅ Allows trusted Microsoft services } @description('Name of the first project') param projectName string = '${take(name, 8)}proj' +@description('Whether to include the sample app in the deployment. NOTE: Cosmos and Search must also be enabled and Auth Client ID and Secret must be provided.') +param appSampleEnabled bool + +@description('Client id for registered application in Entra for use with app authentication.') +param authClientId string? + +@secure() +@description('Client secret for registered application in Entra for use with app authentication.') +param authClientSecret string? + var defaultTags = { 'azd-env-name': name } @@ -112,6 +122,18 @@ var allTags = union(defaultTags, tags) var resourceToken = substring(uniqueString(subscription().id, location, name), 0, 5) var servicesUsername = take(replace(vmAdminUsername,'.', ''), 20) +var deploySampleApp = appSampleEnabled && cosmosDbEnabled && searchEnabled && !empty(authClientId) && !empty(authClientSecret) && !empty(cosmosDatabases) && !empty(aiGPTModelDeployment) && length(aiEmbeddingModelDeployment) >= 2 +var authClientSecretName = 'auth-client-secret' + +module appIdentity 'br/public:avm/res/managed-identity/user-assigned-identity:0.4.1' = if (deploySampleApp) { + name: take('${name}-identity-deployment', 64) + params: { + name: toLower('id-app-${name}') + location: location + tags: allTags + } +} + module logAnalyticsWorkspace 'br/public:avm/res/operational-insights/workspace:0.11.0' = { name: take('${name}-log-analytics-deployment', 64) params: { @@ -136,27 +158,9 @@ module applicationInsights 'br/public:avm/res/insights/component:0.6.0' = { module network 'modules/virtualNetwork.bicep' = if (networkIsolation) { name: take('${name}-network-deployment', 64) params: { - virtualNetworkName: toLower('vnet-${name}') - virtualNetworkAddressPrefixes: '10.0.0.0/8' - vmSubnetName: toLower('snet-${name}-vm') - vmSubnetAddressPrefix: '10.3.1.0/24' - vmSubnetNsgName: toLower('nsg-snet-${name}-vm') - bastionHostEnabled: true - bastionSubnetAddressPrefix: '10.3.2.0/24' - bastionSubnetNsgName: 'nsg-AzureBastionSubnet' - bastionHostName: toLower('bas-${name}') - bastionHostDisableCopyPaste: false - bastionHostEnableFileCopy: true - bastionHostEnableIpConnect: true - bastionHostEnableShareableLink: true - bastionHostEnableTunneling: true - bastionPublicIpAddressName: toLower('pip-bas-${name}') - bastionHostSkuName: 'Standard' - natGatewayName: toLower('nat-${name}') - natGatewayPublicIps: 1 - natGatewayIdleTimeoutMins: 30 + resourceToken: resourceToken allowedIpAddress: allowedIpAddress - workspaceId: logAnalyticsWorkspace.outputs.resourceId + logAnalyticsWorkspaceId: logAnalyticsWorkspace.outputs.resourceId location: location tags: allTags } @@ -168,10 +172,28 @@ module keyvault 'modules/keyvault.bicep' = { name: 'kv${name}${resourceToken}' location: location networkIsolation: networkIsolation - virtualNetworkResourceId: networkIsolation ? network.outputs.virtualNetworkId : '' - virtualNetworkSubnetResourceId: networkIsolation ? network.outputs.vmSubnetId : '' + virtualNetworkResourceId: networkIsolation ? network.outputs.resourceId : '' + virtualNetworkSubnetResourceId: networkIsolation ? network.outputs.defaultSubnetResourceId : '' logAnalyticsWorkspaceResourceId: logAnalyticsWorkspace.outputs.resourceId - userObjectId: userObjectId + roleAssignments: concat(empty(userObjectId) ? [] : [ + { + principalId: userObjectId + principalType: 'User' + roleDefinitionIdOrName: 'Key Vault Secrets User' + } + ], deploySampleApp ? [ + { + principalId: appIdentity.outputs.principalId + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Key Vault Secrets User' + } + ] : []) + secrets: deploySampleApp ? [ + { + name: authClientSecretName + value: authClientSecret ?? '' + } + ] : [] tags: allTags } } @@ -182,8 +204,8 @@ module containerRegistry 'modules/containerRegistry.bicep' = if (acrEnabled) { name: 'cr${name}${resourceToken}' location: location networkIsolation: networkIsolation - virtualNetworkResourceId: networkIsolation ? network.outputs.virtualNetworkId : '' - virtualNetworkSubnetResourceId: networkIsolation ? network.outputs.vmSubnetId : '' + virtualNetworkResourceId: networkIsolation ? network.outputs.resourceId : '' + virtualNetworkSubnetResourceId: networkIsolation ? network.outputs.defaultSubnetResourceId : '' logAnalyticsWorkspaceResourceId: logAnalyticsWorkspace.outputs.resourceId tags: allTags } @@ -195,8 +217,8 @@ module storageAccount 'modules/storageAccount.bicep' = { storageName: 'st${name}${resourceToken}' location: location networkIsolation: networkIsolation - virtualNetworkResourceId: networkIsolation ? network.outputs.virtualNetworkId : '' - virtualNetworkSubnetResourceId: networkIsolation ? network.outputs.vmSubnetId : '' + virtualNetworkResourceId: networkIsolation ? network.outputs.resourceId : '' + virtualNetworkSubnetResourceId: networkIsolation ? network.outputs.defaultSubnetResourceId : '' logAnalyticsWorkspaceResourceId: logAnalyticsWorkspace.outputs.resourceId roleAssignments: concat(empty(userObjectId) ? [] : [ { @@ -229,8 +251,9 @@ module cognitiveServices 'modules/cognitive-services/main.bicep' = { location: aiDeploymentsLocation networkIsolation: networkIsolation networkAcls: networkAcls - virtualNetworkResourceId: networkIsolation ? network.outputs.virtualNetworkId : '' - virtualNetworkSubnetResourceId: networkIsolation ? network.outputs.vmSubnetId : '' + virtualNetworkResourceId: networkIsolation ? network.outputs.resourceId : '' + virtualNetworkSubnetResourceId: networkIsolation ? network.outputs.defaultSubnetResourceId : '' + principalIds: deploySampleApp ? [appIdentity.outputs.principalId] : [] logAnalyticsWorkspaceResourceId: logAnalyticsWorkspace.outputs.resourceId aiModelDeployments: [ for model in [aiEmbeddingModelDeployment, aiGPTModelDeployment]: { @@ -278,8 +301,8 @@ module aiSearch 'modules/aisearch.bicep' = if (searchEnabled) { name: 'srch${name}${resourceToken}' location: location networkIsolation: networkIsolation - virtualNetworkResourceId: networkIsolation ? network.outputs.virtualNetworkId : '' - virtualNetworkSubnetResourceId: networkIsolation ? network.outputs.vmSubnetId : '' + virtualNetworkResourceId: networkIsolation ? network.outputs.resourceId : '' + virtualNetworkSubnetResourceId: networkIsolation ? network.outputs.defaultSubnetResourceId : '' logAnalyticsWorkspaceResourceId: logAnalyticsWorkspace.outputs.resourceId roleAssignments: union(empty(userObjectId) ? [] : [ { @@ -314,7 +337,7 @@ module virtualMachine './modules/virtualMachine.bicep' = if (networkIsolation) vmName: toLower('vm-${name}-jump') vmNicName: toLower('nic-vm-${name}-jump') vmSize: vmSize - vmSubnetId: network.outputs.vmSubnetId + vmSubnetId: network.outputs.defaultSubnetResourceId storageAccountName: storageAccount.outputs.storageName storageAccountResourceGroup: resourceGroup().name imagePublisher: 'MicrosoftWindowsDesktop' @@ -338,7 +361,6 @@ module virtualMachine './modules/virtualMachine.bicep' = if (networkIsolation) dependsOn: networkIsolation ? [storageAccount] : [] } - module apim 'modules/apim.bicep' = if (apiManagementEnabled) { name: take('${name}-apim-deployment', 64) params: { @@ -349,8 +371,8 @@ module apim 'modules/apim.bicep' = if (apiManagementEnabled) { sku: 'Developer' networkIsolation: networkIsolation logAnalyticsWorkspaceResourceId: logAnalyticsWorkspace.outputs.resourceId - virtualNetworkResourceId: networkIsolation ? network.outputs.virtualNetworkId : '' - virtualNetworkSubnetResourceId: networkIsolation ? network.outputs.vmSubnetId : '' + virtualNetworkResourceId: networkIsolation ? network.outputs.resourceId : '' + virtualNetworkSubnetResourceId: networkIsolation ? network.outputs.defaultSubnetResourceId : '' tags: allTags } } @@ -361,10 +383,11 @@ module cosmosDb 'modules/cosmosDb.bicep' = if (cosmosDbEnabled) { name: 'cos${name}${resourceToken}' location: location networkIsolation: networkIsolation - virtualNetworkResourceId: networkIsolation ? network.outputs.virtualNetworkId : '' - virtualNetworkSubnetResourceId: networkIsolation ? network.outputs.vmSubnetId : '' + virtualNetworkResourceId: networkIsolation ? network.outputs.resourceId : '' + virtualNetworkSubnetResourceId: networkIsolation ? network.outputs.defaultSubnetResourceId : '' logAnalyticsWorkspaceResourceId: logAnalyticsWorkspace.outputs.resourceId databases: cosmosDatabases + sqlRoleAssignmentsPrincipalIds: deploySampleApp ? [appIdentity.outputs.principalId] : [] tags: allTags } } @@ -378,15 +401,70 @@ module sqlServer 'modules/sqlServer.bicep' = if (sqlServerEnabled) { databases: sqlServerDatabases location: location networkIsolation: networkIsolation - virtualNetworkResourceId: networkIsolation ? network.outputs.virtualNetworkId : '' - virtualNetworkSubnetResourceId: networkIsolation ? network.outputs.vmSubnetId : '' + virtualNetworkResourceId: networkIsolation ? network.outputs.resourceId : '' + virtualNetworkSubnetResourceId: networkIsolation ? network.outputs.defaultSubnetResourceId : '' tags: allTags } } -import { sqlDatabaseType, databasePropertyType, modelDeploymentType } from 'modules/customTypes.bicep' +module appService 'modules/appservice.bicep' = if (deploySampleApp) { + name: take('${name}-app-service-deployment', 64) + params: { + name: 'app-${name}${resourceToken}' + location: location + userAssignedIdentityName: appIdentity.outputs.name + appInsightsName: applicationInsights.outputs.name + keyVaultName: keyvault.outputs.name + logAnalyticsWorkspaceResourceId: logAnalyticsWorkspace.outputs.resourceId + skuName: 'B3' + skuCapacity: 1 + imagePath: 'sampleappaoaichatgpt.azurecr.io/sample-app-aoai-chatgpt' + imageTag: '2025-02-13_52' + virtualNetworkSubnetId: networkIsolation ? network.outputs.appSubnetResourceId : '' + authProvider: { + clientId: authClientId ?? '' + clientSecretName: authClientSecretName + openIdIssuer: '${environment().authentication.loginEndpoint}${tenant().tenantId}/v2.0' + } + searchServiceConfiguration: { + name: aiSearch.outputs.name + indexName: 'ai_app_index' + } + cosmosDbConfiguration: { + account: cosmosDb.outputs.cosmosDBname + database: cosmosDatabases[0].name + container: cosmosDatabases[0].?containers[0].?name ?? '' + } + openAIConfiguration: { + name: cognitiveServices.outputs.aiServicesName + endpoint: cognitiveServices.outputs.aiServicesEndpoint + gptModelName: aiGPTModelDeployment.modelName + gptModelDeploymentName: aiGPTModelDeployment.modelName // GPT model is second item in array from parameters + embeddingModelDeploymentName: aiEmbeddingModelDeployment.modelName // Embedding model is first item in array from parameters + } + } +} + +module appSample './modules/vmscriptsetup.bicep' = if (deploySampleApp) { + name: 'app-sample-deployment' + params: { + aiSearchName: searchEnabled ? aiSearch.outputs.name : '' + cognitiveServicesName: cognitiveServices.outputs.aiServicesName + aiEmbeddingModelDeployment: aiEmbeddingModelDeployment + networkIsolation: networkIsolation + virtualMachinePrincipalId: networkIsolation ? virtualMachine.outputs.principalId : '' + vmName: networkIsolation ? virtualMachine.outputs.name : '' + } + dependsOn: [ + keyvault + ] +} +import { sqlDatabaseType, databasePropertyType, modelDeploymentType } from 'modules/customTypes.bicep' +output AZURE_SEARCH_ENDPOINT string = searchEnabled ? 'https://${aiSearch.outputs.name}.search.windows.net' : '' +output AZURE_OPENAI_ENDPOINT string = cognitiveServices.outputs.aiServicesEndpoint +output EMBEDDING_MODEL_NAME string = aiEmbeddingModelDeployment.modelName output AZURE_KEY_VAULT_NAME string = keyvault.outputs.name output AZURE_AI_SERVICES_NAME string = cognitiveServices.outputs.aiServicesName output AZURE_AI_SEARCH_NAME string = searchEnabled ? aiSearch.outputs.name : '' @@ -400,8 +478,10 @@ output AZURE_CONTAINER_REGISTRY_NAME string = acrEnabled ? containerRegistry.out output AZURE_LOG_ANALYTICS_WORKSPACE_NAME string = logAnalyticsWorkspace.outputs.name output AZURE_STORAGE_ACCOUNT_NAME string = storageAccount.outputs.storageName output AZURE_API_MANAGEMENT_NAME string = apiManagementEnabled ? apim.outputs.name : '' -output AZURE_VIRTUAL_NETWORK_NAME string = networkIsolation ? network.outputs.virtualNetworkName : '' -output AZURE_VIRTUAL_NETWORK_SUBNET_NAME string =networkIsolation ? network.outputs.vmSubnetName : '' +output AZURE_VIRTUAL_NETWORK_NAME string = networkIsolation ? network.outputs.name : '' +output AZURE_VIRTUAL_NETWORK_SUBNET_NAME string =networkIsolation ? network.outputs.defaultSubnetName : '' output AZURE_SQL_SERVER_NAME string = sqlServerEnabled ? sqlServer.outputs.name : '' output AZURE_SQL_SERVER_USERNAME string = sqlServerEnabled ? servicesUsername : '' output AZURE_COSMOS_ACCOUNT_NAME string = cosmosDbEnabled ? cosmosDb.outputs.cosmosDBname : '' +output SAMPLE_APP_URL string = deploySampleApp ? appService.outputs.uri : '' +output AZURE_APP_SAMPLE_ENABLED bool = deploySampleApp diff --git a/infra/main.json b/infra/main.json index 9e4a3ae..d648f4e 100644 --- a/infra/main.json +++ b/infra/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.36.1.42791", - "templateHash": "4273186265838174664" + "templateHash": "12092550075038962203" } }, "definitions": { @@ -846,7 +846,6 @@ }, "acrEnabled": { "type": "bool", - "defaultValue": false, "metadata": { "description": "Specifies whether creating an Azure Container Registry." } @@ -898,7 +897,6 @@ }, "apiManagementEnabled": { "type": "bool", - "defaultValue": false, "metadata": { "description": "Specifies if Microsoft APIM is deployed." } @@ -912,14 +910,12 @@ }, "networkIsolation": { "type": "bool", - "defaultValue": true, "metadata": { "description": "Specifies whether network isolation is enabled. When true, Foundry and related components will be deployed, network access parameters will be set to Disabled." } }, "cosmosDbEnabled": { "type": "bool", - "defaultValue": false, "metadata": { "description": "Whether to include Cosmos DB in the deployment." } @@ -936,7 +932,6 @@ }, "sqlServerEnabled": { "type": "bool", - "defaultValue": false, "metadata": { "description": "Whether to include SQL Server in the deployment." } @@ -953,49 +948,42 @@ }, "searchEnabled": { "type": "bool", - "defaultValue": false, "metadata": { "description": "Whether to include Azure AI Search in the deployment." } }, "contentSafetyEnabled": { "type": "bool", - "defaultValue": false, "metadata": { "description": "Whether to include Azure AI Content Safety in the deployment." } }, "visionEnabled": { "type": "bool", - "defaultValue": false, "metadata": { "description": "Whether to include Azure AI Vision in the deployment." } }, "languageEnabled": { "type": "bool", - "defaultValue": false, "metadata": { "description": "Whether to include Azure AI Language in the deployment." } }, "speechEnabled": { "type": "bool", - "defaultValue": false, "metadata": { "description": "Whether to include Azure AI Speech in the deployment." } }, "translatorEnabled": { "type": "bool", - "defaultValue": false, "metadata": { "description": "Whether to include Azure AI Translator in the deployment." } }, "documentIntelligenceEnabled": { "type": "bool", - "defaultValue": false, "metadata": { "description": "Whether to include Azure Document Intelligence in the deployment." } @@ -1003,7 +991,7 @@ "networkAcls": { "type": "object", "defaultValue": { - "defaultAction": "Deny", + "defaultAction": "[if(parameters('networkIsolation'), 'Deny', 'Allow')]", "bypass": "AzureServices" }, "metadata": { @@ -1016,6 +1004,26 @@ "metadata": { "description": "Name of the first project" } + }, + "appSampleEnabled": { + "type": "bool", + "metadata": { + "description": "Whether to include the sample app in the deployment. NOTE: Cosmos and Search must also be enabled and Auth Client ID and Secret must be provided." + } + }, + "authClientId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Client id for registered application in Entra for use with app authentication." + } + }, + "authClientSecret": { + "type": "securestring", + "nullable": true, + "metadata": { + "description": "Client secret for registered application in Entra for use with app authentication." + } } }, "variables": { @@ -1024,9 +1032,491 @@ }, "allTags": "[union(variables('defaultTags'), parameters('tags'))]", "resourceToken": "[substring(uniqueString(subscription().id, parameters('location'), parameters('name')), 0, 5)]", - "servicesUsername": "[take(replace(parameters('vmAdminUsername'), '.', ''), 20)]" + "servicesUsername": "[take(replace(parameters('vmAdminUsername'), '.', ''), 20)]", + "deploySampleApp": "[and(and(and(and(and(and(and(parameters('appSampleEnabled'), parameters('cosmosDbEnabled')), parameters('searchEnabled')), not(empty(parameters('authClientId')))), not(empty(parameters('authClientSecret')))), not(empty(parameters('cosmosDatabases')))), not(empty(parameters('aiGPTModelDeployment')))), greaterOrEquals(length(parameters('aiEmbeddingModelDeployment')), 2))]", + "authClientSecretName": "auth-client-secret" }, "resources": { + "appIdentity": { + "condition": "[variables('deploySampleApp')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('{0}-identity-deployment', parameters('name')), 64)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[toLower(format('id-app-{0}', parameters('name')))]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('allTags')]" + } + }, + "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.34.44.8038", + "templateHash": "16707109626832623586" + }, + "name": "User Assigned Identities", + "description": "This module deploys a User Assigned Identity." + }, + "definitions": { + "federatedIdentityCredentialType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the federated identity credential." + } + }, + "audiences": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The list of audiences that can appear in the issued token." + } + }, + "issuer": { + "type": "string", + "metadata": { + "description": "Required. The URL of the issuer to be trusted." + } + }, + "subject": { + "type": "string", + "metadata": { + "description": "Required. The identifier of the external identity." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for the federated identity credential." + } + }, + "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." + } + } + }, + "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.5.1" + } + } + }, + "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.5.1" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the User Assigned Identity." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "federatedIdentityCredentials": { + "type": "array", + "items": { + "$ref": "#/definitions/federatedIdentityCredentialType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The federated identity credentials list to indicate which token from the external IdP should be trusted by your application. Federated identity credentials are supported on applications only. A maximum of 20 federated identity credentials can be added per application object." + } + }, + "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", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "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')]", + "Managed Identity Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e40ec5ca-96e0-45a2-b4ff-59039f2c2b59')]", + "Managed Identity Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f1a07417-d97a-45cb-824c-7a7467783830')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.managedidentity-userassignedidentity.{0}.{1}', replace('0.4.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" + } + } + } + } + }, + "userAssignedIdentity": { + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2024-11-30", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]" + }, + "userAssignedIdentity_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.ManagedIdentity/userAssignedIdentities/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "userAssignedIdentity" + ] + }, + "userAssignedIdentity_roleAssignments": { + "copy": { + "name": "userAssignedIdentity_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.ManagedIdentity/userAssignedIdentities/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', 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": [ + "userAssignedIdentity" + ] + }, + "userAssignedIdentity_federatedIdentityCredentials": { + "copy": { + "name": "userAssignedIdentity_federatedIdentityCredentials", + "count": "[length(coalesce(parameters('federatedIdentityCredentials'), createArray()))]", + "mode": "serial", + "batchSize": 1 + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-UserMSI-FederatedIdentityCred-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(parameters('federatedIdentityCredentials'), createArray())[copyIndex()].name]" + }, + "userAssignedIdentityName": { + "value": "[parameters('name')]" + }, + "audiences": { + "value": "[coalesce(parameters('federatedIdentityCredentials'), createArray())[copyIndex()].audiences]" + }, + "issuer": { + "value": "[coalesce(parameters('federatedIdentityCredentials'), createArray())[copyIndex()].issuer]" + }, + "subject": { + "value": "[coalesce(parameters('federatedIdentityCredentials'), createArray())[copyIndex()].subject]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.34.44.8038", + "templateHash": "13656021764446440473" + }, + "name": "User Assigned Identity Federated Identity Credential", + "description": "This module deploys a User Assigned Identity Federated Identity Credential." + }, + "parameters": { + "userAssignedIdentityName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent user assigned identity. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the secret." + } + }, + "audiences": { + "type": "array", + "metadata": { + "description": "Required. The list of audiences that can appear in the issued token. Should be set to api://AzureADTokenExchange for Azure AD. It says what Microsoft identity platform should accept in the aud claim in the incoming token. This value represents Azure AD in your external identity provider and has no fixed value across identity providers - you might need to create a new application registration in your IdP to serve as the audience of this token." + } + }, + "issuer": { + "type": "string", + "metadata": { + "description": "Required. The URL of the issuer to be trusted. Must match the issuer claim of the external token being exchanged." + } + }, + "subject": { + "type": "string", + "metadata": { + "description": "Required. The identifier of the external software workload within the external identity provider. Like the audience value, it has no fixed format, as each IdP uses their own - sometimes a GUID, sometimes a colon delimited identifier, sometimes arbitrary strings. The value here must match the sub claim within the token presented to Azure AD." + } + } + }, + "resources": [ + { + "type": "Microsoft.ManagedIdentity/userAssignedIdentities/federatedIdentityCredentials", + "apiVersion": "2024-11-30", + "name": "[format('{0}/{1}', parameters('userAssignedIdentityName'), parameters('name'))]", + "properties": { + "audiences": "[parameters('audiences')]", + "issuer": "[parameters('issuer')]", + "subject": "[parameters('subject')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the federated identity credential." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the federated identity credential." + }, + "value": "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities/federatedIdentityCredentials', parameters('userAssignedIdentityName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the federated identity credential was created in." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "userAssignedIdentity" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the user assigned identity." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the user assigned identity." + }, + "value": "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('name'))]" + }, + "principalId": { + "type": "string", + "metadata": { + "description": "The principal ID (object ID) of the user assigned identity." + }, + "value": "[reference('userAssignedIdentity').principalId]" + }, + "clientId": { + "type": "string", + "metadata": { + "description": "The client ID (application ID) of the user assigned identity." + }, + "value": "[reference('userAssignedIdentity').clientId]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the user assigned identity was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('userAssignedIdentity', '2024-11-30', 'full').location]" + } + } + } + } + }, "logAnalyticsWorkspace": { "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", @@ -4778,67 +5268,13 @@ }, "mode": "Incremental", "parameters": { - "virtualNetworkName": { - "value": "[toLower(format('vnet-{0}', parameters('name')))]" - }, - "virtualNetworkAddressPrefixes": { - "value": "10.0.0.0/8" - }, - "vmSubnetName": { - "value": "[toLower(format('snet-{0}-vm', parameters('name')))]" - }, - "vmSubnetAddressPrefix": { - "value": "10.3.1.0/24" - }, - "vmSubnetNsgName": { - "value": "[toLower(format('nsg-snet-{0}-vm', parameters('name')))]" - }, - "bastionHostEnabled": { - "value": true - }, - "bastionSubnetAddressPrefix": { - "value": "10.3.2.0/24" - }, - "bastionSubnetNsgName": { - "value": "nsg-AzureBastionSubnet" - }, - "bastionHostName": { - "value": "[toLower(format('bas-{0}', parameters('name')))]" - }, - "bastionHostDisableCopyPaste": { - "value": false - }, - "bastionHostEnableFileCopy": { - "value": true - }, - "bastionHostEnableIpConnect": { - "value": true - }, - "bastionHostEnableShareableLink": { - "value": true - }, - "bastionHostEnableTunneling": { - "value": true - }, - "bastionPublicIpAddressName": { - "value": "[toLower(format('pip-bas-{0}', parameters('name')))]" - }, - "bastionHostSkuName": { - "value": "Standard" - }, - "natGatewayName": { - "value": "[toLower(format('nat-{0}', parameters('name')))]" - }, - "natGatewayPublicIps": { - "value": 1 - }, - "natGatewayIdleTimeoutMins": { - "value": 30 + "resourceToken": { + "value": "[variables('resourceToken')]" }, "allowedIpAddress": { "value": "[parameters('allowedIpAddress')]" }, - "workspaceId": { + "logAnalyticsWorkspaceId": { "value": "[reference('logAnalyticsWorkspace').outputs.resourceId.value]" }, "location": { @@ -4855,144 +5291,14 @@ "_generator": { "name": "bicep", "version": "0.36.1.42791", - "templateHash": "14424734402412352330" + "templateHash": "14803697760422194567" } }, "parameters": { - "virtualNetworkName": { - "type": "string", - "metadata": { - "description": "Specifies the name of the virtual network." - } - }, - "virtualNetworkAddressPrefixes": { - "type": "string", - "defaultValue": "10.0.0.0/8", - "metadata": { - "description": "Specifies the address prefixes of the virtual network." - } - }, - "vmSubnetName": { - "type": "string", - "defaultValue": "VmSubnet", - "metadata": { - "description": "Specifies the name of the subnet which contains the virtual machine." - } - }, - "vmSubnetAddressPrefix": { - "type": "string", - "defaultValue": "10.3.1.0/24", - "metadata": { - "description": "Specifies the address prefix of the subnet which contains the virtual machine." - } - }, - "vmSubnetNsgName": { - "type": "string", - "defaultValue": "VmSubnetNsg", - "metadata": { - "description": "Specifies the name of the network security group associated to the subnet hosting the virtual machine." - } - }, - "bastionSubnetAddressPrefix": { - "type": "string", - "defaultValue": "10.3.2.0/24", - "metadata": { - "description": "Specifies the Bastion subnet IP prefix. This prefix must be within vnet IP prefix address space." - } - }, - "bastionSubnetNsgName": { - "type": "string", - "defaultValue": "AzureBastionNsg", - "metadata": { - "description": "Specifies the name of the network security group associated to the subnet hosting Azure Bastion." - } - }, - "bastionHostEnabled": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Specifies whether Azure Bastion should be created." - } - }, - "bastionHostName": { - "type": "string", - "metadata": { - "description": "Specifies the name of the Azure Bastion resource." - } - }, - "bastionHostDisableCopyPaste": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Enable/Disable Copy/Paste feature of the Bastion Host resource." - } - }, - "bastionHostEnableFileCopy": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Enable/Disable File Copy feature of the Bastion Host resource." - } - }, - "bastionHostEnableIpConnect": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Enable/Disable IP Connect feature of the Bastion Host resource." - } - }, - "bastionHostEnableShareableLink": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Enable/Disable Shareable Link of the Bastion Host resource." - } - }, - "bastionHostEnableTunneling": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Enable/Disable Tunneling feature of the Bastion Host resource." - } - }, - "bastionPublicIpAddressName": { - "type": "string", - "metadata": { - "description": "Specifies the name of the Azure Public IP Address used by the Azure Bastion Host." - } - }, - "bastionHostSkuName": { - "type": "string", - "defaultValue": "Standard", - "metadata": { - "description": "Specifies the name of the Azure Bastion Host SKU." - } - }, - "natGatewayName": { + "resourceToken": { "type": "string", "metadata": { - "description": "Specifies the name of the Azure NAT Gateway." - } - }, - "natGatewayZones": { - "type": "array", - "defaultValue": [], - "metadata": { - "description": "Specifies a list of availability zones denoting the zone in which Nat Gateway should be deployed." - } - }, - "natGatewayPublicIps": { - "type": "int", - "defaultValue": 1, - "metadata": { - "description": "Specifies the number of Public IPs to create for the Azure NAT Gateway." - } - }, - "natGatewayIdleTimeoutMins": { - "type": "int", - "defaultValue": 30, - "metadata": { - "description": "Specifies the idle timeout in minutes for the Azure NAT Gateway." + "description": "Specifies the name used to name networking Azure resources." } }, "allowedIpAddress": { @@ -5002,7 +5308,7 @@ "description": "Optional IP address to allow access throught Bastion NSG. If not specified, all IP addresses are allowed." } }, - "workspaceId": { + "logAnalyticsWorkspaceId": { "type": "string", "metadata": { "description": "Specifies the resource id of the Log Analytics workspace." @@ -5010,595 +5316,28 @@ }, "location": { "type": "string", - "defaultValue": "[resourceGroup().location]", "metadata": { "description": "Specifies the location." } }, - "tags": { - "type": "object", - "metadata": { - "description": "Specifies the resource tags." - } - } - }, - "variables": { - "copy": [ - { - "name": "nsgLogs", - "count": "[length(variables('nsgLogCategories'))]", - "input": { - "category": "[variables('nsgLogCategories')[copyIndex('nsgLogs')]]", - "enabled": true, - "retentionPolicy": { - "enabled": true, - "days": 0 - } - } - }, - { - "name": "vnetLogs", - "count": "[length(variables('vnetLogCategories'))]", - "input": { - "category": "[variables('vnetLogCategories')[copyIndex('vnetLogs')]]", - "enabled": true, - "retentionPolicy": { - "enabled": true, - "days": 0 - } - } - }, - { - "name": "vnetMetrics", - "count": "[length(variables('vnetMetricCategories'))]", - "input": { - "category": "[variables('vnetMetricCategories')[copyIndex('vnetMetrics')]]", - "enabled": true, - "retentionPolicy": { - "enabled": true, - "days": 0 - } - } - }, - { - "name": "bastionLogs", - "count": "[length(variables('bastionLogCategories'))]", - "input": { - "category": "[variables('bastionLogCategories')[copyIndex('bastionLogs')]]", - "enabled": true, - "retentionPolicy": { - "enabled": true, - "days": 0 - } - } - }, - { - "name": "bastionMetrics", - "count": "[length(variables('bastionMetricCategories'))]", - "input": { - "category": "[variables('bastionMetricCategories')[copyIndex('bastionMetrics')]]", - "enabled": true, - "retentionPolicy": { - "enabled": true, - "days": 0 - } - } - } - ], - "diagnosticSettingsName": "diagnosticSettings", - "nsgLogCategories": [ - "NetworkSecurityGroupEvent", - "NetworkSecurityGroupRuleCounter" - ], - "vnetLogCategories": [ - "VMProtectionAlerts" - ], - "vnetMetricCategories": [ - "AllMetrics" - ], - "bastionLogCategories": [ - "BastionAuditLogs" - ], - "bastionMetricCategories": [ - "AllMetrics" - ], - "bastionSubnetName": "AzureBastionSubnet" - }, - "resources": [ - { - "type": "Microsoft.Network/virtualNetworks", - "apiVersion": "2024-03-01", - "name": "[parameters('virtualNetworkName')]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", - "properties": { - "addressSpace": { - "addressPrefixes": [ - "[parameters('virtualNetworkAddressPrefixes')]" - ] - }, - "subnets": [ - { - "name": "[parameters('vmSubnetName')]", - "properties": { - "addressPrefix": "[parameters('vmSubnetAddressPrefix')]", - "networkSecurityGroup": { - "id": "[resourceId('Microsoft.Network/networkSecurityGroups', parameters('vmSubnetNsgName'))]" - }, - "privateEndpointNetworkPolicies": "Disabled", - "privateLinkServiceNetworkPolicies": "Disabled", - "natGateway": { - "id": "[resourceId('Microsoft.Network/natGateways', parameters('natGatewayName'))]" - } - } - }, - { - "name": "[variables('bastionSubnetName')]", - "properties": { - "addressPrefix": "[parameters('bastionSubnetAddressPrefix')]", - "networkSecurityGroup": { - "id": "[resourceId('Microsoft.Network/networkSecurityGroups', parameters('bastionSubnetNsgName'))]" - } - } - } - ] - }, - "dependsOn": [ - "[resourceId('Microsoft.Network/networkSecurityGroups', parameters('bastionSubnetNsgName'))]", - "[resourceId('Microsoft.Network/natGateways', parameters('natGatewayName'))]", - "[resourceId('Microsoft.Network/networkSecurityGroups', parameters('vmSubnetNsgName'))]" - ] - }, - { - "condition": "[parameters('bastionHostEnabled')]", - "type": "Microsoft.Network/networkSecurityGroups", - "apiVersion": "2023-04-01", - "name": "[parameters('bastionSubnetNsgName')]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", - "properties": { - "securityRules": [ - { - "name": "AllowHttpsInBound", - "properties": { - "protocol": "Tcp", - "sourcePortRange": "*", - "sourceAddressPrefix": "[if(empty(parameters('allowedIpAddress')), 'Internet', parameters('allowedIpAddress'))]", - "destinationPortRange": "443", - "destinationAddressPrefix": "*", - "access": "Allow", - "priority": 100, - "direction": "Inbound" - } - }, - { - "name": "AllowGatewayManagerInBound", - "properties": { - "protocol": "Tcp", - "sourcePortRange": "*", - "sourceAddressPrefix": "GatewayManager", - "destinationPortRange": "443", - "destinationAddressPrefix": "*", - "access": "Allow", - "priority": 110, - "direction": "Inbound" - } - }, - { - "name": "AllowLoadBalancerInBound", - "properties": { - "protocol": "Tcp", - "sourcePortRange": "*", - "sourceAddressPrefix": "AzureLoadBalancer", - "destinationPortRange": "443", - "destinationAddressPrefix": "*", - "access": "Allow", - "priority": 120, - "direction": "Inbound" - } - }, - { - "name": "AllowBastionHostCommunicationInBound", - "properties": { - "protocol": "*", - "sourcePortRange": "*", - "sourceAddressPrefix": "VirtualNetwork", - "destinationPortRanges": [ - "8080", - "5701" - ], - "destinationAddressPrefix": "VirtualNetwork", - "access": "Allow", - "priority": 130, - "direction": "Inbound" - } - }, - { - "name": "DenyAllInBound", - "properties": { - "protocol": "*", - "sourcePortRange": "*", - "sourceAddressPrefix": "*", - "destinationPortRange": "*", - "destinationAddressPrefix": "*", - "access": "Deny", - "priority": 1000, - "direction": "Inbound" - } - }, - { - "name": "AllowSshRdpOutBound", - "properties": { - "protocol": "Tcp", - "sourcePortRange": "*", - "sourceAddressPrefix": "*", - "destinationPortRanges": [ - "22", - "3389" - ], - "destinationAddressPrefix": "VirtualNetwork", - "access": "Allow", - "priority": 100, - "direction": "Outbound" - } - }, - { - "name": "AllowAzureCloudCommunicationOutBound", - "properties": { - "protocol": "Tcp", - "sourcePortRange": "*", - "sourceAddressPrefix": "*", - "destinationPortRange": "443", - "destinationAddressPrefix": "AzureCloud", - "access": "Allow", - "priority": 110, - "direction": "Outbound" - } - }, - { - "name": "AllowBastionHostCommunicationOutBound", - "properties": { - "protocol": "*", - "sourcePortRange": "*", - "sourceAddressPrefix": "VirtualNetwork", - "destinationPortRanges": [ - "8080", - "5701" - ], - "destinationAddressPrefix": "VirtualNetwork", - "access": "Allow", - "priority": 120, - "direction": "Outbound" - } - }, - { - "name": "AllowGetSessionInformationOutBound", - "properties": { - "protocol": "*", - "sourcePortRange": "*", - "sourceAddressPrefix": "*", - "destinationAddressPrefix": "Internet", - "destinationPortRanges": [ - "80", - "443" - ], - "access": "Allow", - "priority": 130, - "direction": "Outbound" - } - }, - { - "name": "DenyAllOutBound", - "properties": { - "protocol": "*", - "sourcePortRange": "*", - "destinationPortRange": "*", - "sourceAddressPrefix": "*", - "destinationAddressPrefix": "*", - "access": "Deny", - "priority": 1000, - "direction": "Outbound" - } - } - ] - } - }, - { - "type": "Microsoft.Network/networkSecurityGroups", - "apiVersion": "2023-04-01", - "name": "[parameters('vmSubnetNsgName')]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", - "properties": { - "securityRules": [] - } - }, - { - "copy": { - "name": "natGatewayPublicIp", - "count": "[length(range(0, parameters('natGatewayPublicIps')))]" - }, - "type": "Microsoft.Network/publicIPAddresses", - "apiVersion": "2023-04-01", - "name": "[if(equals(parameters('natGatewayPublicIps'), 1), format('{0}PublicIp', parameters('natGatewayName')), format('{0}PublicIp{1}', parameters('natGatewayName'), add(range(0, parameters('natGatewayPublicIps'))[copyIndex()], 1)))]", - "location": "[parameters('location')]", - "sku": { - "name": "Standard" - }, - "zones": "[if(not(empty(parameters('natGatewayZones'))), parameters('natGatewayZones'), createArray())]", - "properties": { - "publicIPAllocationMethod": "Static" - } - }, - { - "type": "Microsoft.Network/natGateways", - "apiVersion": "2024-03-01", - "name": "[parameters('natGatewayName')]", - "location": "[parameters('location')]", - "sku": { - "name": "Standard" - }, - "zones": "[if(not(empty(parameters('natGatewayZones'))), parameters('natGatewayZones'), createArray())]", - "properties": { - "copy": [ - { - "name": "publicIpAddresses", - "count": "[length(range(0, parameters('natGatewayPublicIps')))]", - "input": { - "id": "[resourceId('Microsoft.Network/publicIPAddresses', if(equals(parameters('natGatewayPublicIps'), 1), format('{0}PublicIp', parameters('natGatewayName')), format('{0}PublicIp{1}', parameters('natGatewayName'), add(range(0, parameters('natGatewayPublicIps'))[range(0, parameters('natGatewayPublicIps'))[copyIndex('publicIpAddresses')]], 1))))]" - } - } - ], - "idleTimeoutInMinutes": "[parameters('natGatewayIdleTimeoutMins')]" - }, - "dependsOn": [ - "natGatewayPublicIp" - ] - }, - { - "condition": "[parameters('bastionHostEnabled')]", - "type": "Microsoft.Network/publicIPAddresses", - "apiVersion": "2023-04-01", - "name": "[parameters('bastionPublicIpAddressName')]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", - "sku": { - "name": "Standard" - }, - "properties": { - "publicIPAllocationMethod": "Static" - } - }, - { - "condition": "[parameters('bastionHostEnabled')]", - "type": "Microsoft.Network/bastionHosts", - "apiVersion": "2023-04-01", - "name": "[parameters('bastionHostName')]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", - "sku": { - "name": "[parameters('bastionHostSkuName')]" - }, - "properties": { - "disableCopyPaste": "[parameters('bastionHostDisableCopyPaste')]", - "enableFileCopy": "[parameters('bastionHostEnableFileCopy')]", - "enableIpConnect": "[parameters('bastionHostEnableIpConnect')]", - "enableShareableLink": "[parameters('bastionHostEnableShareableLink')]", - "enableTunneling": "[parameters('bastionHostEnableTunneling')]", - "ipConfigurations": [ - { - "name": "IpConf", - "properties": { - "subnet": { - "id": "[format('{0}/subnets/{1}', resourceId('Microsoft.Network/virtualNetworks', parameters('virtualNetworkName')), variables('bastionSubnetName'))]" - }, - "publicIPAddress": { - "id": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('bastionPublicIpAddressName'))]" - } - } - } - ] - }, - "dependsOn": [ - "[resourceId('Microsoft.Network/publicIPAddresses', parameters('bastionPublicIpAddressName'))]", - "[resourceId('Microsoft.Network/virtualNetworks', parameters('virtualNetworkName'))]" - ] - }, - { - "type": "Microsoft.Insights/diagnosticSettings", - "apiVersion": "2021-05-01-preview", - "scope": "[format('Microsoft.Network/networkSecurityGroups/{0}', parameters('vmSubnetNsgName'))]", - "name": "[variables('diagnosticSettingsName')]", - "properties": { - "workspaceId": "[parameters('workspaceId')]", - "logs": "[variables('nsgLogs')]" - }, - "dependsOn": [ - "[resourceId('Microsoft.Network/networkSecurityGroups', parameters('vmSubnetNsgName'))]" - ] - }, - { - "condition": "[parameters('bastionHostEnabled')]", - "type": "Microsoft.Insights/diagnosticSettings", - "apiVersion": "2021-05-01-preview", - "scope": "[format('Microsoft.Network/networkSecurityGroups/{0}', parameters('bastionSubnetNsgName'))]", - "name": "[variables('diagnosticSettingsName')]", - "properties": { - "workspaceId": "[parameters('workspaceId')]", - "logs": "[variables('nsgLogs')]" - }, - "dependsOn": [ - "[resourceId('Microsoft.Network/networkSecurityGroups', parameters('bastionSubnetNsgName'))]" - ] - }, - { - "type": "Microsoft.Insights/diagnosticSettings", - "apiVersion": "2021-05-01-preview", - "scope": "[format('Microsoft.Network/virtualNetworks/{0}', parameters('virtualNetworkName'))]", - "name": "[variables('diagnosticSettingsName')]", - "properties": { - "workspaceId": "[parameters('workspaceId')]", - "logs": "[variables('vnetLogs')]", - "metrics": "[variables('vnetMetrics')]" - }, - "dependsOn": [ - "[resourceId('Microsoft.Network/virtualNetworks', parameters('virtualNetworkName'))]" - ] - }, - { - "condition": "[parameters('bastionHostEnabled')]", - "type": "Microsoft.Insights/diagnosticSettings", - "apiVersion": "2021-05-01-preview", - "scope": "[format('Microsoft.Network/bastionHosts/{0}', parameters('bastionHostName'))]", - "name": "[variables('diagnosticSettingsName')]", - "properties": { - "workspaceId": "[parameters('workspaceId')]", - "logs": "[variables('bastionLogs')]", - "metrics": "[variables('bastionMetrics')]" - }, - "dependsOn": [ - "[resourceId('Microsoft.Network/bastionHosts', parameters('bastionHostName'))]" - ] - } - ], - "outputs": { - "virtualNetworkId": { - "type": "string", - "value": "[resourceId('Microsoft.Network/virtualNetworks', parameters('virtualNetworkName'))]" - }, - "virtualNetworkName": { - "type": "string", - "value": "[parameters('virtualNetworkName')]" - }, - "vmSubnetId": { - "type": "string", - "value": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('vmSubnetName'))]" - }, - "bastionSubnetId": { - "type": "string", - "value": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), variables('bastionSubnetName'))]" - }, - "vmSubnetName": { - "type": "string", - "value": "[parameters('vmSubnetName')]" - }, - "bastionSubnetName": { - "type": "string", - "value": "[variables('bastionSubnetName')]" - }, - "bastionName": { - "type": "string", - "value": "[parameters('bastionHostName')]" - } - } - } - }, - "dependsOn": [ - "logAnalyticsWorkspace" - ] - }, - "keyvault": { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[take(format('{0}-keyvault-deployment', parameters('name')), 64)]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[format('kv{0}{1}', parameters('name'), variables('resourceToken'))]" - }, - "location": { - "value": "[parameters('location')]" - }, - "networkIsolation": { - "value": "[parameters('networkIsolation')]" - }, - "virtualNetworkResourceId": "[if(parameters('networkIsolation'), createObject('value', reference('network').outputs.virtualNetworkId.value), createObject('value', ''))]", - "virtualNetworkSubnetResourceId": "[if(parameters('networkIsolation'), createObject('value', reference('network').outputs.vmSubnetId.value), createObject('value', ''))]", - "logAnalyticsWorkspaceResourceId": { - "value": "[reference('logAnalyticsWorkspace').outputs.resourceId.value]" - }, - "userObjectId": { - "value": "[parameters('userObjectId')]" - }, - "tags": { - "value": "[variables('allTags')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "11746621349310683559" - } - }, - "parameters": { - "name": { - "type": "string", - "metadata": { - "description": "Name of the Key Vault." - } - }, - "location": { - "type": "string", - "metadata": { - "description": "Specifies the location for all the Azure resources." - } - }, "tags": { "type": "object", "defaultValue": {}, "metadata": { - "description": "Optional. Tags to be applied to the resources." - } - }, - "virtualNetworkResourceId": { - "type": "string", - "metadata": { - "description": "Resource ID of the virtual network to link the private DNS zones." - } - }, - "virtualNetworkSubnetResourceId": { - "type": "string", - "metadata": { - "description": "Resource ID of the subnet for the private endpoint." - } - }, - "logAnalyticsWorkspaceResourceId": { - "type": "string", - "metadata": { - "description": "Resource ID of the Log Analytics workspace to use for diagnostic settings." - } - }, - "networkIsolation": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Specifies whether network isolation is enabled. This will create a private endpoint for the Key Vault and link the private DNS zone." - } - }, - "userObjectId": { - "type": "string", - "metadata": { - "description": "Specifies the object id of a Microsoft Entra ID user. In general, this the object id of the system administrator who deploys the Azure resources. This defaults to the deploying user." + "description": "Specifies the resource tags." } } }, "variables": { - "nameFormatted": "[take(toLower(parameters('name')), 24)]" + "bastionSubnetName": "AzureBastionSubnet", + "defaultSubnetName": "snet-default", + "appSubnetName": "snet-web-apps" }, "resources": [ { - "condition": "[parameters('networkIsolation')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "private-dns-keyvault-deployment", + "name": "[take(format('{0}-bastion-nsg', parameters('resourceToken')), 64)]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -5606,17 +5345,166 @@ "mode": "Incremental", "parameters": { "name": { - "value": "[format('privatelink.{0}', if(equals(toLower(environment().name), 'azureusgovernment'), 'vaultcore.usgovcloudapi.net', 'vaultcore.azure.net'))]" + "value": "[format('nsg-{0}', variables('bastionSubnetName'))]" }, - "virtualNetworkLinks": { + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "diagnosticSettings": { "value": [ { - "virtualNetworkResourceId": "[parameters('virtualNetworkResourceId')]" + "workspaceResourceId": "[parameters('logAnalyticsWorkspaceId')]" } ] }, - "tags": { - "value": "[parameters('tags')]" + "securityRules": { + "value": [ + { + "name": "AllowHttpsInBound", + "properties": { + "protocol": "Tcp", + "sourcePortRange": "*", + "sourceAddressPrefix": "[if(empty(parameters('allowedIpAddress')), 'Internet', parameters('allowedIpAddress'))]", + "destinationPortRange": "443", + "destinationAddressPrefix": "*", + "access": "Allow", + "priority": 100, + "direction": "Inbound" + } + }, + { + "name": "AllowGatewayManagerInBound", + "properties": { + "protocol": "Tcp", + "sourcePortRange": "*", + "sourceAddressPrefix": "GatewayManager", + "destinationPortRange": "443", + "destinationAddressPrefix": "*", + "access": "Allow", + "priority": 110, + "direction": "Inbound" + } + }, + { + "name": "AllowLoadBalancerInBound", + "properties": { + "protocol": "Tcp", + "sourcePortRange": "*", + "sourceAddressPrefix": "AzureLoadBalancer", + "destinationPortRange": "443", + "destinationAddressPrefix": "*", + "access": "Allow", + "priority": 120, + "direction": "Inbound" + } + }, + { + "name": "AllowBastionHostCommunicationInBound", + "properties": { + "protocol": "*", + "sourcePortRange": "*", + "sourceAddressPrefix": "VirtualNetwork", + "destinationPortRanges": [ + "8080", + "5701" + ], + "destinationAddressPrefix": "VirtualNetwork", + "access": "Allow", + "priority": 130, + "direction": "Inbound" + } + }, + { + "name": "DenyAllInBound", + "properties": { + "protocol": "*", + "sourcePortRange": "*", + "sourceAddressPrefix": "*", + "destinationPortRange": "*", + "destinationAddressPrefix": "*", + "access": "Deny", + "priority": 1000, + "direction": "Inbound" + } + }, + { + "name": "AllowSshRdpOutBound", + "properties": { + "protocol": "Tcp", + "sourcePortRange": "*", + "sourceAddressPrefix": "*", + "destinationPortRanges": [ + "22", + "3389" + ], + "destinationAddressPrefix": "VirtualNetwork", + "access": "Allow", + "priority": 100, + "direction": "Outbound" + } + }, + { + "name": "AllowAzureCloudCommunicationOutBound", + "properties": { + "protocol": "Tcp", + "sourcePortRange": "*", + "sourceAddressPrefix": "*", + "destinationPortRange": "443", + "destinationAddressPrefix": "AzureCloud", + "access": "Allow", + "priority": 110, + "direction": "Outbound" + } + }, + { + "name": "AllowBastionHostCommunicationOutBound", + "properties": { + "protocol": "*", + "sourcePortRange": "*", + "sourceAddressPrefix": "VirtualNetwork", + "destinationPortRanges": [ + "8080", + "5701" + ], + "destinationAddressPrefix": "VirtualNetwork", + "access": "Allow", + "priority": 120, + "direction": "Outbound" + } + }, + { + "name": "AllowGetSessionInformationOutBound", + "properties": { + "protocol": "*", + "sourcePortRange": "*", + "sourceAddressPrefix": "*", + "destinationAddressPrefix": "Internet", + "destinationPortRanges": [ + "80", + "443" + ], + "access": "Allow", + "priority": 130, + "direction": "Outbound" + } + }, + { + "name": "DenyAllOutBound", + "properties": { + "protocol": "*", + "sourcePortRange": "*", + "destinationPortRange": "*", + "sourceAddressPrefix": "*", + "destinationAddressPrefix": "*", + "access": "Deny", + "priority": 1000, + "direction": "Outbound" + } + } + ] } }, "template": { @@ -5626,86 +5514,269 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "83178825086050429" + "version": "0.33.93.31351", + "templateHash": "2305747478751645177" }, - "name": "Private DNS Zones", - "description": "This module deploys a Private DNS zone.", - "owner": "Azure/module-maintainers" + "name": "Network Security Groups", + "description": "This module deploys a Network security Group (NSG)." }, "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "type": "object", + "securityRuleType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the security rule." + } + }, "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\"." + "type": "object", + "properties": { + "access": { + "type": "string", + "allowedValues": [ + "Allow", + "Deny" + ], + "metadata": { + "description": "Required. Whether network traffic is allowed or denied." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the security rule." + } + }, + "destinationAddressPrefix": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Optional. The destination address prefix. CIDR or destination IP range. Asterisk \"*\" can also be used to match all source IPs. Default tags such as \"VirtualNetwork\", \"AzureLoadBalancer\" and \"Internet\" can also be used." + } + }, + "destinationAddressPrefixes": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The destination address prefixes. CIDR or destination IP ranges." + } + }, + "destinationApplicationSecurityGroupResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The resource IDs of the application security groups specified as destination." + } + }, + "destinationPortRange": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The destination port or range. Integer or range between 0 and 65535. Asterisk \"*\" can also be used to match all ports." + } + }, + "destinationPortRanges": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The destination port ranges." + } + }, + "direction": { + "type": "string", + "allowedValues": [ + "Inbound", + "Outbound" + ], + "metadata": { + "description": "Required. The direction of the rule. The direction specifies if rule will be evaluated on incoming or outgoing traffic." + } + }, + "priority": { + "type": "int", + "minValue": 100, + "maxValue": 4096, + "metadata": { + "description": "Required. Required. The priority of the rule. The value can be between 100 and 4096. The priority number must be unique for each rule in the collection. The lower the priority number, the higher the priority of the rule." + } + }, + "protocol": { + "type": "string", + "allowedValues": [ + "*", + "Ah", + "Esp", + "Icmp", + "Tcp", + "Udp" + ], + "metadata": { + "description": "Required. Network protocol this rule applies to." + } + }, + "sourceAddressPrefix": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The CIDR or source IP range. Asterisk \"*\" can also be used to match all source IPs. Default tags such as \"VirtualNetwork\", \"AzureLoadBalancer\" and \"Internet\" can also be used. If this is an ingress rule, specifies where network traffic originates from." + } + }, + "sourceAddressPrefixes": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The CIDR or source IP ranges." + } + }, + "sourceApplicationSecurityGroupResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The resource IDs of the application security groups specified as source." + } + }, + "sourcePortRange": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The source port or range. Integer or range between 0 and 65535. Asterisk \"*\" can also be used to match all ports." + } + }, + "sourcePortRanges": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The source port ranges." + } } }, - "conditionVersion": { - "type": "string", - "allowedValues": [ - "2.0" - ], - "nullable": true, - "metadata": { - "description": "Optional. Version of the condition." + "metadata": { + "description": "Required. The properties of the security rule." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type of a security rule." + } + }, + "diagnosticSettingLogsOnlyType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } } }, - "delegatedManagedIdentityResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The Resource Id of the delegated managed identity resource." - } + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log 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." } } }, - "nullable": true + "metadata": { + "description": "An AVM-aligned type for a diagnostic setting. To be used if only logs are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } }, "lockType": { "type": "object", @@ -5730,616 +5801,143 @@ } } }, - "nullable": true + "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.5.1" + } + } }, - "aType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } - }, + "roleAssignmentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } - }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "aRecords": { - "type": "array", - "items": { - "type": "object", - "properties": { - "ipv4Address": { - "type": "string", - "metadata": { - "description": "Required. The IPv4 address of this A record." - } - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. The list of A records in the record set." - } + "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." } } }, - "nullable": true + "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.5.1" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the Network Security Group." + } }, - "aaaaType": { + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "securityRules": { "type": "array", "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } - }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "aaaaRecords": { - "type": "array", - "items": { - "type": "object", - "properties": { - "ipv6Address": { - "type": "string", - "metadata": { - "description": "Required. The IPv6 address of this AAAA record." - } - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. The list of AAAA records in the record set." - } - } - } + "$ref": "#/definitions/securityRuleType" }, - "nullable": true + "nullable": true, + "metadata": { + "description": "Optional. Array of Security Rules to deploy to the Network Security Group. When not provided, an NSG including only the built-in roles will be deployed." + } }, - "cnameType": { + "flushConnection": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. When enabled, flows created from Network Security Group connections will be re-evaluated when rules are updates. Initial enablement will trigger re-evaluation. Network Security Group connection flushing is not available in all regions." + } + }, + "diagnosticSettings": { "type": "array", "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } - }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "cnameRecord": { - "type": "object", - "properties": { - "cname": { - "type": "string", - "metadata": { - "description": "Required. The canonical name of the CNAME record." - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. The CNAME record in the record set." - } - } - } + "$ref": "#/definitions/diagnosticSettingLogsOnlyType" }, - "nullable": true + "nullable": true, + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } }, - "mxType": { + "lock": { + "$ref": "#/definitions/lockType", + "nullable": true, + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "roleAssignments": { "type": "array", "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } - }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "mxRecords": { - "type": "array", - "items": { - "type": "object", - "properties": { - "exchange": { - "type": "string", - "metadata": { - "description": "Required. The domain name of the mail host for this MX record." - } - }, - "preference": { - "type": "int", - "metadata": { - "description": "Required. The preference value for this MX record." - } - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. The list of MX records in the record set." - } - } - } - }, - "nullable": true - }, - "ptrType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } - }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "ptrRecords": { - "type": "array", - "items": { - "type": "object", - "properties": { - "ptrdname": { - "type": "string", - "metadata": { - "description": "Required. The PTR target domain name for this PTR record." - } - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. The list of PTR records in the record set." - } - } - } - }, - "nullable": true - }, - "soaType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } - }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "soaRecord": { - "type": "object", - "properties": { - "email": { - "type": "string", - "metadata": { - "description": "Required. The email contact for this SOA record." - } - }, - "expireTime": { - "type": "int", - "metadata": { - "description": "Required. The expire time for this SOA record." - } - }, - "host": { - "type": "string", - "metadata": { - "description": "Required. The domain name of the authoritative name server for this SOA record." - } - }, - "minimumTtl": { - "type": "int", - "metadata": { - "description": "Required. The minimum value for this SOA record. By convention this is used to determine the negative caching duration." - } - }, - "refreshTime": { - "type": "int", - "metadata": { - "description": "Required. The refresh value for this SOA record." - } - }, - "retryTime": { - "type": "int", - "metadata": { - "description": "Required. The retry time for this SOA record." - } - }, - "serialNumber": { - "type": "int", - "metadata": { - "description": "Required. The serial number for this SOA record." - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. The SOA record in the record set." - } - } - } - }, - "nullable": true - }, - "srvType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } - }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "srvRecords": { - "type": "array", - "items": { - "type": "object", - "properties": { - "priority": { - "type": "int", - "metadata": { - "description": "Required. The priority value for this SRV record." - } - }, - "weight": { - "type": "int", - "metadata": { - "description": "Required. The weight value for this SRV record." - } - }, - "port": { - "type": "int", - "metadata": { - "description": "Required. The port value for this SRV record." - } - }, - "target": { - "type": "string", - "metadata": { - "description": "Required. The target domain name for this SRV record." - } - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. The list of SRV records in the record set." - } - } - } - }, - "nullable": true - }, - "txtType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } - }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "txtRecords": { - "type": "array", - "items": { - "type": "object", - "properties": { - "value": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. The text value of this TXT record." - } - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. The list of TXT records in the record set." - } - } - } - }, - "nullable": true - }, - "virtualNetworkLinkType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "minLength": 1, - "maxLength": 80, - "metadata": { - "description": "Optional. The resource name." - } - }, - "virtualNetworkResourceId": { - "type": "string", - "metadata": { - "description": "Required. The resource ID of the virtual network to link." - } - }, - "location": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The Azure Region where the resource lives." - } - }, - "registrationEnabled": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Is auto-registration of virtual machine records in the virtual network in the Private DNS zone enabled?." - } - }, - "tags": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Resource tags." - } - }, - "resolutionPolicy": { - "type": "string", - "allowedValues": [ - "Default", - "NxDomainRedirect" - ], - "nullable": true, - "metadata": { - "description": "Optional. The resolution type of the private-dns-zone fallback machanism." - } - } - } + "$ref": "#/definitions/roleAssignmentType" }, - "nullable": true - } - }, - "parameters": { - "name": { - "type": "string", - "metadata": { - "description": "Required. Private DNS zone name." - } - }, - "a": { - "$ref": "#/definitions/aType", - "metadata": { - "description": "Optional. Array of A records." - } - }, - "aaaa": { - "$ref": "#/definitions/aaaaType", - "metadata": { - "description": "Optional. Array of AAAA records." - } - }, - "cname": { - "$ref": "#/definitions/cnameType", - "metadata": { - "description": "Optional. Array of CNAME records." - } - }, - "mx": { - "$ref": "#/definitions/mxType", - "metadata": { - "description": "Optional. Array of MX records." - } - }, - "ptr": { - "$ref": "#/definitions/ptrType", - "metadata": { - "description": "Optional. Array of PTR records." - } - }, - "soa": { - "$ref": "#/definitions/soaType", - "metadata": { - "description": "Optional. Array of SOA records." - } - }, - "srv": { - "$ref": "#/definitions/srvType", - "metadata": { - "description": "Optional. Array of SRV records." - } - }, - "txt": { - "$ref": "#/definitions/txtType", - "metadata": { - "description": "Optional. Array of TXT records." - } - }, - "virtualNetworkLinks": { - "$ref": "#/definitions/virtualNetworkLinkType", - "metadata": { - "description": "Optional. Array of custom objects describing vNet links of the DNS zone. Each object should contain properties 'virtualNetworkResourceId' and 'registrationEnabled'. The 'vnetResourceId' is a resource ID of a vNet to link, 'registrationEnabled' (bool) enables automatic DNS registration in the zone for the linked vNet." - } - }, - "location": { - "type": "string", - "defaultValue": "global", - "metadata": { - "description": "Optional. The location of the PrivateDNSZone. Should be global." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", + "nullable": true, "metadata": { "description": "Optional. Array of role assignments to create." } @@ -6348,13 +5946,7 @@ "type": "object", "nullable": true, "metadata": { - "description": "Optional. Tags of the resource." - } - }, - "lock": { - "$ref": "#/definitions/lockType", - "metadata": { - "description": "Optional. The lock settings of the service." + "description": "Optional. Tags of the NSG resource." } }, "enableTelemetry": { @@ -6377,9 +5969,9 @@ "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "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')]" + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, "resources": { @@ -6387,7 +5979,7 @@ "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2024-03-01", - "name": "[format('46d3xbcp.res.network-privatednszone.{0}.{1}', replace('0.7.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "name": "[format('46d3xbcp.res.network-networksecuritygroup.{0}.{1}', replace('0.5.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -6403,36 +5995,97 @@ } } }, - "privateDnsZone": { - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", + "networkSecurityGroup": { + "type": "Microsoft.Network/networkSecurityGroups", + "apiVersion": "2023-11-01", "name": "[parameters('name')]", "location": "[parameters('location')]", - "tags": "[parameters('tags')]" + "tags": "[parameters('tags')]", + "properties": { + "copy": [ + { + "name": "securityRules", + "count": "[length(coalesce(parameters('securityRules'), createArray()))]", + "input": { + "name": "[coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].name]", + "properties": { + "access": "[coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties.access]", + "description": "[coalesce(tryGet(coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties, 'description'), '')]", + "destinationAddressPrefix": "[coalesce(tryGet(coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties, 'destinationAddressPrefix'), '')]", + "destinationAddressPrefixes": "[coalesce(tryGet(coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties, 'destinationAddressPrefixes'), createArray())]", + "destinationApplicationSecurityGroups": "[map(coalesce(tryGet(coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties, 'destinationApplicationSecurityGroupResourceIds'), createArray()), lambda('destinationApplicationSecurityGroupResourceId', createObject('id', lambdaVariables('destinationApplicationSecurityGroupResourceId'))))]", + "destinationPortRange": "[coalesce(tryGet(coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties, 'destinationPortRange'), '')]", + "destinationPortRanges": "[coalesce(tryGet(coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties, 'destinationPortRanges'), createArray())]", + "direction": "[coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties.direction]", + "priority": "[coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties.priority]", + "protocol": "[coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties.protocol]", + "sourceAddressPrefix": "[coalesce(tryGet(coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties, 'sourceAddressPrefix'), '')]", + "sourceAddressPrefixes": "[coalesce(tryGet(coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties, 'sourceAddressPrefixes'), createArray())]", + "sourceApplicationSecurityGroups": "[map(coalesce(tryGet(coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties, 'sourceApplicationSecurityGroupResourceIds'), createArray()), lambda('sourceApplicationSecurityGroupResourceId', createObject('id', lambdaVariables('sourceApplicationSecurityGroupResourceId'))))]", + "sourcePortRange": "[coalesce(tryGet(coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties, 'sourcePortRange'), '')]", + "sourcePortRanges": "[coalesce(tryGet(coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties, 'sourcePortRanges'), createArray())]" + } + } + } + ], + "flushConnection": "[parameters('flushConnection')]" + } }, - "privateDnsZone_lock": { + "networkSecurityGroup_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/privateDnsZones/{0}', parameters('name'))]", + "scope": "[format('Microsoft.Network/networkSecurityGroups/{0}', parameters('name'))]", "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", "properties": { "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" }, "dependsOn": [ - "privateDnsZone" + "networkSecurityGroup" ] }, - "privateDnsZone_roleAssignments": { + "networkSecurityGroup_diagnosticSettings": { "copy": { - "name": "privateDnsZone_roleAssignments", + "name": "networkSecurityGroup_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Network/networkSecurityGroups/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "networkSecurityGroup" + ] + }, + "networkSecurityGroup_roleAssignments": { + "copy": { + "name": "networkSecurityGroup_roleAssignments", "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}', parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "scope": "[format('Microsoft.Network/networkSecurityGroups/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/networkSecurityGroups', 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]", @@ -6443,2164 +6096,1319 @@ "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" }, "dependsOn": [ - "privateDnsZone" + "networkSecurityGroup" ] - }, - "privateDnsZone_A": { - "copy": { - "name": "privateDnsZone_A", - "count": "[length(coalesce(parameters('a'), createArray()))]" + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the network security group was deployed into." }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-ARecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the network security group." + }, + "value": "[resourceId('Microsoft.Network/networkSecurityGroups', parameters('name'))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the network security group." + }, + "value": "[parameters('name')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('networkSecurityGroup', '2023-11-01', 'full').location]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('{0}-default-nsg', parameters('resourceToken')), 64)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[format('nsg-{0}', variables('defaultSubnetName'))]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "diagnosticSettings": { + "value": [ + { + "workspaceResourceId": "[parameters('logAnalyticsWorkspaceId')]" + } + ] + }, + "securityRules": { + "value": [] + } + }, + "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.33.93.31351", + "templateHash": "2305747478751645177" + }, + "name": "Network Security Groups", + "description": "This module deploys a Network security Group (NSG)." + }, + "definitions": { + "securityRuleType": { + "type": "object", "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "privateDnsZoneName": { - "value": "[parameters('name')]" - }, - "name": { - "value": "[coalesce(parameters('a'), createArray())[copyIndex()].name]" - }, - "aRecords": { - "value": "[tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'aRecords')]" - }, + "name": { + "type": "string", "metadata": { - "value": "[tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'metadata')]" - }, - "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'ttl'), 3600)]" - }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'roleAssignments')]" + "description": "Required. The name of the security rule." } }, - "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.32.4.45862", - "templateHash": "2531120132215940282" - }, - "name": "Private DNS Zone A record", - "description": "This module deploys a Private DNS Zone A record.", - "owner": "Azure/module-maintainers" - }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } - } - } - }, - "nullable": true - } - }, - "parameters": { - "privateDnsZoneName": { + "properties": { + "type": "object", + "properties": { + "access": { "type": "string", + "allowedValues": [ + "Allow", + "Deny" + ], "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + "description": "Required. Whether network traffic is allowed or denied." } }, - "name": { + "description": { "type": "string", + "nullable": true, "metadata": { - "description": "Required. The name of the A record." + "description": "Optional. The description of the security rule." } }, - "aRecords": { - "type": "array", + "destinationAddressPrefix": { + "type": "string", "nullable": true, "metadata": { - "description": "Optional. The list of A records in the record set." + "description": "Optional. Optional. The destination address prefix. CIDR or destination IP range. Asterisk \"*\" can also be used to match all source IPs. Default tags such as \"VirtualNetwork\", \"AzureLoadBalancer\" and \"Internet\" can also be used." } }, - "metadata": { - "type": "object", + "destinationAddressPrefixes": { + "type": "array", + "items": { + "type": "string" + }, "nullable": true, "metadata": { - "description": "Optional. The metadata attached to the record set." + "description": "Optional. The destination address prefixes. CIDR or destination IP ranges." } }, - "ttl": { - "type": "int", - "defaultValue": 3600, + "destinationApplicationSecurityGroupResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." + "description": "Optional. The resource IDs of the application security groups specified as destination." } }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", + "destinationPortRange": { + "type": "string", + "nullable": true, "metadata": { - "description": "Optional. Array of role assignments to create." - } - } - }, - "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')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", - "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" - } - }, - "resources": { - "privateDnsZone": { - "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" - }, - "A": { - "type": "Microsoft.Network/privateDnsZones/A", - "apiVersion": "2020-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "properties": { - "aRecords": "[parameters('aRecords')]", - "metadata": "[parameters('metadata')]", - "ttl": "[parameters('ttl')]" + "description": "Optional. The destination port or range. Integer or range between 0 and 65535. Asterisk \"*\" can also be used to match all ports." } }, - "A_roleAssignments": { - "copy": { - "name": "A_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/A/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/A', parameters('privateDnsZoneName'), 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')]" + "destinationPortRanges": { + "type": "array", + "items": { + "type": "string" }, - "dependsOn": [ - "A" - ] - } - }, - "outputs": { - "name": { - "type": "string", + "nullable": true, "metadata": { - "description": "The name of the deployed A record." - }, - "value": "[parameters('name')]" + "description": "Optional. The destination port ranges." + } }, - "resourceId": { + "direction": { "type": "string", + "allowedValues": [ + "Inbound", + "Outbound" + ], "metadata": { - "description": "The resource ID of the deployed A record." - }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/A', parameters('privateDnsZoneName'), parameters('name'))]" + "description": "Required. The direction of the rule. The direction specifies if rule will be evaluated on incoming or outgoing traffic." + } }, - "resourceGroupName": { - "type": "string", + "priority": { + "type": "int", + "minValue": 100, + "maxValue": 4096, "metadata": { - "description": "The resource group of the deployed A record." - }, - "value": "[resourceGroup().name]" - } - } - } - }, - "dependsOn": [ - "privateDnsZone" - ] - }, - "privateDnsZone_AAAA": { - "copy": { - "name": "privateDnsZone_AAAA", - "count": "[length(coalesce(parameters('aaaa'), createArray()))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-AAAARecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "privateDnsZoneName": { - "value": "[parameters('name')]" - }, - "name": { - "value": "[coalesce(parameters('aaaa'), createArray())[copyIndex()].name]" - }, - "aaaaRecords": { - "value": "[tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'aaaaRecords')]" - }, - "metadata": { - "value": "[tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'metadata')]" - }, - "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'ttl'), 3600)]" - }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'roleAssignments')]" - } - }, - "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.32.4.45862", - "templateHash": "16709340450244912125" + "description": "Required. Required. The priority of the rule. The value can be between 100 and 4096. The priority number must be unique for each rule in the collection. The lower the priority number, the higher the priority of the rule." + } }, - "name": "Private DNS Zone AAAA record", - "description": "This module deploys a Private DNS Zone AAAA record.", - "owner": "Azure/module-maintainers" - }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } - } - } - }, - "nullable": true - } - }, - "parameters": { - "privateDnsZoneName": { + "protocol": { "type": "string", + "allowedValues": [ + "*", + "Ah", + "Esp", + "Icmp", + "Tcp", + "Udp" + ], "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + "description": "Required. Network protocol this rule applies to." } }, - "name": { + "sourceAddressPrefix": { "type": "string", + "nullable": true, "metadata": { - "description": "Required. The name of the AAAA record." + "description": "Optional. The CIDR or source IP range. Asterisk \"*\" can also be used to match all source IPs. Default tags such as \"VirtualNetwork\", \"AzureLoadBalancer\" and \"Internet\" can also be used. If this is an ingress rule, specifies where network traffic originates from." } }, - "aaaaRecords": { + "sourceAddressPrefixes": { "type": "array", + "items": { + "type": "string" + }, "nullable": true, "metadata": { - "description": "Optional. The list of AAAA records in the record set." + "description": "Optional. The CIDR or source IP ranges." } }, - "metadata": { - "type": "object", + "sourceApplicationSecurityGroupResourceIds": { + "type": "array", + "items": { + "type": "string" + }, "nullable": true, "metadata": { - "description": "Optional. The metadata attached to the record set." + "description": "Optional. The resource IDs of the application security groups specified as source." } }, - "ttl": { - "type": "int", - "defaultValue": 3600, + "sourcePortRange": { + "type": "string", + "nullable": true, "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." + "description": "Optional. The source port or range. Integer or range between 0 and 65535. Asterisk \"*\" can also be used to match all ports." } }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", + "sourcePortRanges": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, "metadata": { - "description": "Optional. Array of role assignments to create." - } - } - }, - "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)))))]" + "description": "Optional. The source port ranges." } - ], - "builtInRoleNames": { - "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", - "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, - "resources": { - "privateDnsZone": { - "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" - }, - "AAAA": { - "type": "Microsoft.Network/privateDnsZones/AAAA", - "apiVersion": "2020-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "properties": { - "aaaaRecords": "[parameters('aaaaRecords')]", - "metadata": "[parameters('metadata')]", - "ttl": "[parameters('ttl')]" - } - }, - "AAAA_roleAssignments": { - "copy": { - "name": "AAAA_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/AAAA/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/AAAA', parameters('privateDnsZoneName'), 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": [ - "AAAA" - ] - } - }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the deployed AAAA record." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the deployed AAAA record." - }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/AAAA', parameters('privateDnsZoneName'), parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group of the deployed AAAA record." - }, - "value": "[resourceGroup().name]" - } + "metadata": { + "description": "Required. The properties of the security rule." } } }, - "dependsOn": [ - "privateDnsZone" - ] + "metadata": { + "__bicep_export!": true, + "description": "The type of a security rule." + } }, - "privateDnsZone_CNAME": { - "copy": { - "name": "privateDnsZone_CNAME", - "count": "[length(coalesce(parameters('cname'), createArray()))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-CNAMERecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "diagnosticSettingLogsOnlyType": { + "type": "object", "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "privateDnsZoneName": { - "value": "[parameters('name')]" - }, - "name": { - "value": "[coalesce(parameters('cname'), createArray())[copyIndex()].name]" - }, - "cnameRecord": { - "value": "[tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'cnameRecord')]" - }, + "name": { + "type": "string", + "nullable": true, "metadata": { - "value": "[tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'metadata')]" - }, - "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'ttl'), 3600)]" - }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'roleAssignments')]" + "description": "Optional. The name of diagnostic setting." } }, - "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.32.4.45862", - "templateHash": "9976020649752073181" - }, - "name": "Private DNS Zone CNAME record", - "description": "This module deploys a Private DNS Zone CNAME record.", - "owner": "Azure/module-maintainers" - }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } - } + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." } }, - "nullable": true - } - }, - "parameters": { - "privateDnsZoneName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the CNAME record." - } - }, - "cnameRecord": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. A CNAME record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata attached to the record set." - } - }, - "ttl": { - "type": "int", - "defaultValue": 3600, - "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - } - }, - "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')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", - "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" - } - }, - "resources": { - "privateDnsZone": { - "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" - }, - "CNAME": { - "type": "Microsoft.Network/privateDnsZones/CNAME", - "apiVersion": "2020-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "properties": { - "cnameRecord": "[parameters('cnameRecord')]", - "metadata": "[parameters('metadata')]", - "ttl": "[parameters('ttl')]" - } - }, - "CNAME_roleAssignments": { - "copy": { - "name": "CNAME_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/CNAME/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/CNAME', parameters('privateDnsZoneName'), 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')]" + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } }, - "dependsOn": [ - "CNAME" - ] + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } } }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the deployed CNAME record." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the deployed CNAME record." - }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/CNAME', parameters('privateDnsZoneName'), parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group of the deployed CNAME record." - }, - "value": "[resourceGroup().name]" - } + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log 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." } } }, - "dependsOn": [ - "privateDnsZone" - ] + "metadata": { + "description": "An AVM-aligned type for a diagnostic setting. To be used if only logs are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } }, - "privateDnsZone_MX": { - "copy": { - "name": "privateDnsZone_MX", - "count": "[length(coalesce(parameters('mx'), createArray()))]" + "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." + } + } }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-MXRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "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.5.1" + } + } + }, + "roleAssignmentType": { + "type": "object", "properties": { - "expressionEvaluationOptions": { - "scope": "inner" + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } }, - "mode": "Incremental", - "parameters": { - "privateDnsZoneName": { - "value": "[parameters('name')]" - }, - "name": { - "value": "[coalesce(parameters('mx'), createArray())[copyIndex()].name]" - }, + "roleDefinitionIdOrName": { + "type": "string", "metadata": { - "value": "[tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'metadata')]" - }, - "mxRecords": { - "value": "[tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'mxRecords')]" - }, - "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'ttl'), 3600)]" - }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'roleAssignments')]" + "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'." } }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "languageVersion": "2.0", - "contentVersion": "1.0.0.0", + "principalId": { + "type": "string", "metadata": { - "_generator": { - "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "2520323624213076361" - }, - "name": "Private DNS Zone MX record", - "description": "This module deploys a Private DNS Zone MX record.", - "owner": "Azure/module-maintainers" - }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } - } - } - }, - "nullable": true + "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.5.1" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the Network Security Group." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "securityRules": { + "type": "array", + "items": { + "$ref": "#/definitions/securityRuleType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of Security Rules to deploy to the Network Security Group. When not provided, an NSG including only the built-in roles will be deployed." + } + }, + "flushConnection": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. When enabled, flows created from Network Security Group connections will be re-evaluated when rules are updates. Initial enablement will trigger re-evaluation. Network Security Group connection flushing is not available in all regions." + } + }, + "diagnosticSettings": { + "type": "array", + "items": { + "$ref": "#/definitions/diagnosticSettingLogsOnlyType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "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", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the NSG resource." + } + }, + "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')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-networksecuritygroup.{0}.{1}', replace('0.5.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" } - }, - "parameters": { - "privateDnsZoneName": { + } + } + } + }, + "networkSecurityGroup": { + "type": "Microsoft.Network/networkSecurityGroups", + "apiVersion": "2023-11-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "copy": [ + { + "name": "securityRules", + "count": "[length(coalesce(parameters('securityRules'), createArray()))]", + "input": { + "name": "[coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].name]", + "properties": { + "access": "[coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties.access]", + "description": "[coalesce(tryGet(coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties, 'description'), '')]", + "destinationAddressPrefix": "[coalesce(tryGet(coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties, 'destinationAddressPrefix'), '')]", + "destinationAddressPrefixes": "[coalesce(tryGet(coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties, 'destinationAddressPrefixes'), createArray())]", + "destinationApplicationSecurityGroups": "[map(coalesce(tryGet(coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties, 'destinationApplicationSecurityGroupResourceIds'), createArray()), lambda('destinationApplicationSecurityGroupResourceId', createObject('id', lambdaVariables('destinationApplicationSecurityGroupResourceId'))))]", + "destinationPortRange": "[coalesce(tryGet(coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties, 'destinationPortRange'), '')]", + "destinationPortRanges": "[coalesce(tryGet(coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties, 'destinationPortRanges'), createArray())]", + "direction": "[coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties.direction]", + "priority": "[coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties.priority]", + "protocol": "[coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties.protocol]", + "sourceAddressPrefix": "[coalesce(tryGet(coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties, 'sourceAddressPrefix'), '')]", + "sourceAddressPrefixes": "[coalesce(tryGet(coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties, 'sourceAddressPrefixes'), createArray())]", + "sourceApplicationSecurityGroups": "[map(coalesce(tryGet(coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties, 'sourceApplicationSecurityGroupResourceIds'), createArray()), lambda('sourceApplicationSecurityGroupResourceId', createObject('id', lambdaVariables('sourceApplicationSecurityGroupResourceId'))))]", + "sourcePortRange": "[coalesce(tryGet(coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties, 'sourcePortRange'), '')]", + "sourcePortRanges": "[coalesce(tryGet(coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties, 'sourcePortRanges'), createArray())]" + } + } + } + ], + "flushConnection": "[parameters('flushConnection')]" + } + }, + "networkSecurityGroup_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/networkSecurityGroups/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "networkSecurityGroup" + ] + }, + "networkSecurityGroup_diagnosticSettings": { + "copy": { + "name": "networkSecurityGroup_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Network/networkSecurityGroups/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "networkSecurityGroup" + ] + }, + "networkSecurityGroup_roleAssignments": { + "copy": { + "name": "networkSecurityGroup_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/networkSecurityGroups/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/networkSecurityGroups', 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": [ + "networkSecurityGroup" + ] + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the network security group was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the network security group." + }, + "value": "[resourceId('Microsoft.Network/networkSecurityGroups', parameters('name'))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the network security group." + }, + "value": "[parameters('name')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('networkSecurityGroup', '2023-11-01', 'full').location]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('{0}-app-nsg', parameters('resourceToken')), 64)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[format('nsg-{0}', variables('appSubnetName'))]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "diagnosticSettings": { + "value": [ + { + "workspaceResourceId": "[parameters('logAnalyticsWorkspaceId')]" + } + ] + }, + "securityRules": { + "value": [] + } + }, + "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.33.93.31351", + "templateHash": "2305747478751645177" + }, + "name": "Network Security Groups", + "description": "This module deploys a Network security Group (NSG)." + }, + "definitions": { + "securityRuleType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the security rule." + } + }, + "properties": { + "type": "object", + "properties": { + "access": { "type": "string", + "allowedValues": [ + "Allow", + "Deny" + ], "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + "description": "Required. Whether network traffic is allowed or denied." } }, - "name": { + "description": { "type": "string", + "nullable": true, "metadata": { - "description": "Required. The name of the MX record." + "description": "Optional. The description of the security rule." } }, - "metadata": { - "type": "object", + "destinationAddressPrefix": { + "type": "string", "nullable": true, "metadata": { - "description": "Optional. The metadata attached to the record set." + "description": "Optional. Optional. The destination address prefix. CIDR or destination IP range. Asterisk \"*\" can also be used to match all source IPs. Default tags such as \"VirtualNetwork\", \"AzureLoadBalancer\" and \"Internet\" can also be used." } }, - "mxRecords": { + "destinationAddressPrefixes": { "type": "array", + "items": { + "type": "string" + }, "nullable": true, "metadata": { - "description": "Optional. The list of MX records in the record set." + "description": "Optional. The destination address prefixes. CIDR or destination IP ranges." } }, - "ttl": { - "type": "int", - "defaultValue": 3600, + "destinationApplicationSecurityGroupResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." + "description": "Optional. The resource IDs of the application security groups specified as destination." } }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", + "destinationPortRange": { + "type": "string", + "nullable": true, "metadata": { - "description": "Optional. Array of role assignments to create." + "description": "Optional. The destination port or range. Integer or range between 0 and 65535. Asterisk \"*\" can also be used to match all ports." } - } - }, - "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)))))]" + }, + "destinationPortRanges": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The destination port ranges." } - ], - "builtInRoleNames": { - "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", - "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" - } - }, - "resources": { - "privateDnsZone": { - "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" }, - "MX": { - "type": "Microsoft.Network/privateDnsZones/MX", - "apiVersion": "2020-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "properties": { - "metadata": "[parameters('metadata')]", - "mxRecords": "[parameters('mxRecords')]", - "ttl": "[parameters('ttl')]" + "direction": { + "type": "string", + "allowedValues": [ + "Inbound", + "Outbound" + ], + "metadata": { + "description": "Required. The direction of the rule. The direction specifies if rule will be evaluated on incoming or outgoing traffic." } }, - "MX_roleAssignments": { - "copy": { - "name": "MX_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/MX/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/MX', parameters('privateDnsZoneName'), 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": [ - "MX" - ] - } - }, - "outputs": { - "name": { + "priority": { + "type": "int", + "minValue": 100, + "maxValue": 4096, + "metadata": { + "description": "Required. Required. The priority of the rule. The value can be between 100 and 4096. The priority number must be unique for each rule in the collection. The lower the priority number, the higher the priority of the rule." + } + }, + "protocol": { "type": "string", + "allowedValues": [ + "*", + "Ah", + "Esp", + "Icmp", + "Tcp", + "Udp" + ], "metadata": { - "description": "The name of the deployed MX record." - }, - "value": "[parameters('name')]" + "description": "Required. Network protocol this rule applies to." + } }, - "resourceId": { + "sourceAddressPrefix": { "type": "string", + "nullable": true, "metadata": { - "description": "The resource ID of the deployed MX record." + "description": "Optional. The CIDR or source IP range. Asterisk \"*\" can also be used to match all source IPs. Default tags such as \"VirtualNetwork\", \"AzureLoadBalancer\" and \"Internet\" can also be used. If this is an ingress rule, specifies where network traffic originates from." + } + }, + "sourceAddressPrefixes": { + "type": "array", + "items": { + "type": "string" }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/MX', parameters('privateDnsZoneName'), parameters('name'))]" + "nullable": true, + "metadata": { + "description": "Optional. The CIDR or source IP ranges." + } }, - "resourceGroupName": { + "sourceApplicationSecurityGroupResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The resource IDs of the application security groups specified as source." + } + }, + "sourcePortRange": { "type": "string", + "nullable": true, "metadata": { - "description": "The resource group of the deployed MX record." + "description": "Optional. The source port or range. Integer or range between 0 and 65535. Asterisk \"*\" can also be used to match all ports." + } + }, + "sourcePortRanges": { + "type": "array", + "items": { + "type": "string" }, - "value": "[resourceGroup().name]" + "nullable": true, + "metadata": { + "description": "Optional. The source port ranges." + } } + }, + "metadata": { + "description": "Required. The properties of the security rule." } } }, - "dependsOn": [ - "privateDnsZone" - ] + "metadata": { + "__bicep_export!": true, + "description": "The type of a security rule." + } }, - "privateDnsZone_PTR": { - "copy": { - "name": "privateDnsZone_PTR", - "count": "[length(coalesce(parameters('ptr'), createArray()))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-PTRRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "diagnosticSettingLogsOnlyType": { + "type": "object", "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "privateDnsZoneName": { - "value": "[parameters('name')]" - }, - "name": { - "value": "[coalesce(parameters('ptr'), createArray())[copyIndex()].name]" - }, + "name": { + "type": "string", + "nullable": true, "metadata": { - "value": "[tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'metadata')]" - }, - "ptrRecords": { - "value": "[tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'ptrRecords')]" - }, - "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'ttl'), 3600)]" - }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'roleAssignments')]" + "description": "Optional. The name of diagnostic setting." } }, - "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.32.4.45862", - "templateHash": "3080404733048745471" - }, - "name": "Private DNS Zone PTR record", - "description": "This module deploys a Private DNS Zone PTR record.", - "owner": "Azure/module-maintainers" - }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } - } + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." } }, - "nullable": true - } - }, - "parameters": { - "privateDnsZoneName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the PTR record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata attached to the record set." - } - }, - "ptrRecords": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. The list of PTR records in the record set." - } - }, - "ttl": { - "type": "int", - "defaultValue": 3600, - "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - } - }, - "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')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", - "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" - } - }, - "resources": { - "privateDnsZone": { - "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" - }, - "PTR": { - "type": "Microsoft.Network/privateDnsZones/PTR", - "apiVersion": "2020-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "properties": { - "metadata": "[parameters('metadata')]", - "ptrRecords": "[parameters('ptrRecords')]", - "ttl": "[parameters('ttl')]" - } - }, - "PTR_roleAssignments": { - "copy": { - "name": "PTR_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/PTR/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/PTR', parameters('privateDnsZoneName'), 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')]" + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } }, - "dependsOn": [ - "PTR" - ] + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } } }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the deployed PTR record." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the deployed PTR record." - }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/PTR', parameters('privateDnsZoneName'), parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group of the deployed PTR record." - }, - "value": "[resourceGroup().name]" - } + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log 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." } } }, - "dependsOn": [ - "privateDnsZone" - ] + "metadata": { + "description": "An AVM-aligned type for a diagnostic setting. To be used if only logs are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } }, - "privateDnsZone_SOA": { - "copy": { - "name": "privateDnsZone_SOA", - "count": "[length(coalesce(parameters('soa'), createArray()))]" + "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." + } + } }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-SOARecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "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.5.1" + } + } + }, + "roleAssignmentType": { + "type": "object", "properties": { - "expressionEvaluationOptions": { - "scope": "inner" + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } }, - "mode": "Incremental", - "parameters": { - "privateDnsZoneName": { - "value": "[parameters('name')]" - }, - "name": { - "value": "[coalesce(parameters('soa'), createArray())[copyIndex()].name]" - }, + "roleDefinitionIdOrName": { + "type": "string", "metadata": { - "value": "[tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'metadata')]" - }, - "soaRecord": { - "value": "[tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'soaRecord')]" - }, - "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'ttl'), 3600)]" - }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'roleAssignments')]" + "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.5.1" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the Network Security Group." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "securityRules": { + "type": "array", + "items": { + "$ref": "#/definitions/securityRuleType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of Security Rules to deploy to the Network Security Group. When not provided, an NSG including only the built-in roles will be deployed." + } + }, + "flushConnection": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. When enabled, flows created from Network Security Group connections will be re-evaluated when rules are updates. Initial enablement will trigger re-evaluation. Network Security Group connection flushing is not available in all regions." + } + }, + "diagnosticSettings": { + "type": "array", + "items": { + "$ref": "#/definitions/diagnosticSettingLogsOnlyType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "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", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the NSG resource." + } + }, + "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')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-networksecuritygroup.{0}.{1}', replace('0.5.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#", - "languageVersion": "2.0", "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "6653951445614700931" - }, - "name": "Private DNS Zone SOA record", - "description": "This module deploys a Private DNS Zone SOA record.", - "owner": "Azure/module-maintainers" - }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } - } - } - }, - "nullable": true - } - }, - "parameters": { - "privateDnsZoneName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the SOA record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata attached to the record set." - } - }, - "soaRecord": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. A SOA record." - } - }, - "ttl": { - "type": "int", - "defaultValue": 3600, - "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - } - }, - "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')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", - "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" - } - }, - "resources": { - "privateDnsZone": { - "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" - }, - "SOA": { - "type": "Microsoft.Network/privateDnsZones/SOA", - "apiVersion": "2020-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "properties": { - "metadata": "[parameters('metadata')]", - "soaRecord": "[parameters('soaRecord')]", - "ttl": "[parameters('ttl')]" - } - }, - "SOA_roleAssignments": { - "copy": { - "name": "SOA_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/SOA/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/SOA', parameters('privateDnsZoneName'), 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": [ - "SOA" - ] - } - }, + "resources": [], "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the deployed SOA record." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the deployed SOA record." - }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/SOA', parameters('privateDnsZoneName'), parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group of the deployed SOA record." - }, - "value": "[resourceGroup().name]" + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" } } } + } + }, + "networkSecurityGroup": { + "type": "Microsoft.Network/networkSecurityGroups", + "apiVersion": "2023-11-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "copy": [ + { + "name": "securityRules", + "count": "[length(coalesce(parameters('securityRules'), createArray()))]", + "input": { + "name": "[coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].name]", + "properties": { + "access": "[coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties.access]", + "description": "[coalesce(tryGet(coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties, 'description'), '')]", + "destinationAddressPrefix": "[coalesce(tryGet(coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties, 'destinationAddressPrefix'), '')]", + "destinationAddressPrefixes": "[coalesce(tryGet(coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties, 'destinationAddressPrefixes'), createArray())]", + "destinationApplicationSecurityGroups": "[map(coalesce(tryGet(coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties, 'destinationApplicationSecurityGroupResourceIds'), createArray()), lambda('destinationApplicationSecurityGroupResourceId', createObject('id', lambdaVariables('destinationApplicationSecurityGroupResourceId'))))]", + "destinationPortRange": "[coalesce(tryGet(coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties, 'destinationPortRange'), '')]", + "destinationPortRanges": "[coalesce(tryGet(coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties, 'destinationPortRanges'), createArray())]", + "direction": "[coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties.direction]", + "priority": "[coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties.priority]", + "protocol": "[coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties.protocol]", + "sourceAddressPrefix": "[coalesce(tryGet(coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties, 'sourceAddressPrefix'), '')]", + "sourceAddressPrefixes": "[coalesce(tryGet(coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties, 'sourceAddressPrefixes'), createArray())]", + "sourceApplicationSecurityGroups": "[map(coalesce(tryGet(coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties, 'sourceApplicationSecurityGroupResourceIds'), createArray()), lambda('sourceApplicationSecurityGroupResourceId', createObject('id', lambdaVariables('sourceApplicationSecurityGroupResourceId'))))]", + "sourcePortRange": "[coalesce(tryGet(coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties, 'sourcePortRange'), '')]", + "sourcePortRanges": "[coalesce(tryGet(coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties, 'sourcePortRanges'), createArray())]" + } + } + } + ], + "flushConnection": "[parameters('flushConnection')]" + } + }, + "networkSecurityGroup_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/networkSecurityGroups/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" }, "dependsOn": [ - "privateDnsZone" + "networkSecurityGroup" ] }, - "privateDnsZone_SRV": { + "networkSecurityGroup_diagnosticSettings": { "copy": { - "name": "privateDnsZone_SRV", - "count": "[length(coalesce(parameters('srv'), createArray()))]" + "name": "networkSecurityGroup_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-SRVRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Network/networkSecurityGroups/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "privateDnsZoneName": { - "value": "[parameters('name')]" - }, - "name": { - "value": "[coalesce(parameters('srv'), createArray())[copyIndex()].name]" - }, - "metadata": { - "value": "[tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'metadata')]" - }, - "srvRecords": { - "value": "[tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'srvRecords')]" - }, - "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'ttl'), 3600)]" - }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'roleAssignments')]" - } - }, - "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.32.4.45862", - "templateHash": "5790774778713328446" - }, - "name": "Private DNS Zone SRV record", - "description": "This module deploys a Private DNS Zone SRV record.", - "owner": "Azure/module-maintainers" - }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } - } - } - }, - "nullable": true - } - }, - "parameters": { - "privateDnsZoneName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the SRV record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata attached to the record set." - } - }, - "srvRecords": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. The list of SRV records in the record set." - } - }, - "ttl": { - "type": "int", - "defaultValue": 3600, - "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - } - }, - "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')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", - "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" - } - }, - "resources": { - "privateDnsZone": { - "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" - }, - "SRV": { - "type": "Microsoft.Network/privateDnsZones/SRV", - "apiVersion": "2020-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "properties": { - "metadata": "[parameters('metadata')]", - "srvRecords": "[parameters('srvRecords')]", - "ttl": "[parameters('ttl')]" - } - }, - "SRV_roleAssignments": { - "copy": { - "name": "SRV_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/SRV/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/SRV', parameters('privateDnsZoneName'), 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": [ - "SRV" - ] - } - }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the deployed SRV record." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the deployed SRV record." - }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/SRV', parameters('privateDnsZoneName'), parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group of the deployed SRV record." - }, - "value": "[resourceGroup().name]" + "copy": [ + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" } } - } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" }, "dependsOn": [ - "privateDnsZone" + "networkSecurityGroup" ] }, - "privateDnsZone_TXT": { + "networkSecurityGroup_roleAssignments": { "copy": { - "name": "privateDnsZone_TXT", - "count": "[length(coalesce(parameters('txt'), createArray()))]" + "name": "networkSecurityGroup_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-TXTRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/networkSecurityGroups/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/networkSecurityGroups', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "privateDnsZoneName": { - "value": "[parameters('name')]" - }, - "name": { - "value": "[coalesce(parameters('txt'), createArray())[copyIndex()].name]" - }, - "metadata": { - "value": "[tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'metadata')]" - }, - "txtRecords": { - "value": "[tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'txtRecords')]" - }, - "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'ttl'), 3600)]" - }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'roleAssignments')]" - } - }, - "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.32.4.45862", - "templateHash": "1855369119498044639" - }, - "name": "Private DNS Zone TXT record", - "description": "This module deploys a Private DNS Zone TXT record.", - "owner": "Azure/module-maintainers" - }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } - } - } - }, - "nullable": true - } - }, - "parameters": { - "privateDnsZoneName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the TXT record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata attached to the record set." - } - }, - "ttl": { - "type": "int", - "defaultValue": 3600, - "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." - } - }, - "txtRecords": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. The list of TXT records in the record set." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - } - }, - "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')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", - "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" - } - }, - "resources": { - "privateDnsZone": { - "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" - }, - "TXT": { - "type": "Microsoft.Network/privateDnsZones/TXT", - "apiVersion": "2020-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "properties": { - "metadata": "[parameters('metadata')]", - "ttl": "[parameters('ttl')]", - "txtRecords": "[parameters('txtRecords')]" - } - }, - "TXT_roleAssignments": { - "copy": { - "name": "TXT_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/TXT/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/TXT', parameters('privateDnsZoneName'), 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": [ - "TXT" - ] - } - }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the deployed TXT record." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the deployed TXT record." - }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/TXT', parameters('privateDnsZoneName'), parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group of the deployed TXT record." - }, - "value": "[resourceGroup().name]" - } - } - } - }, - "dependsOn": [ - "privateDnsZone" - ] - }, - "privateDnsZone_virtualNetworkLinks": { - "copy": { - "name": "privateDnsZone_virtualNetworkLinks", - "count": "[length(coalesce(parameters('virtualNetworkLinks'), createArray()))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-VirtualNetworkLink-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "privateDnsZoneName": { - "value": "[parameters('name')]" - }, - "name": { - "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'name'), format('{0}-vnetlink', last(split(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()].virtualNetworkResourceId, '/'))))]" - }, - "virtualNetworkResourceId": { - "value": "[coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()].virtualNetworkResourceId]" - }, - "location": { - "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'location'), 'global')]" - }, - "registrationEnabled": { - "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'registrationEnabled'), false())]" - }, - "tags": { - "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" - }, - "resolutionPolicy": { - "value": "[tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'resolutionPolicy')]" - } - }, - "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.32.4.45862", - "templateHash": "15326596012552051215" - }, - "name": "Private DNS Zone Virtual Network Link", - "description": "This module deploys a Private DNS Zone Virtual Network Link.", - "owner": "Azure/module-maintainers" - }, - "parameters": { - "privateDnsZoneName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", - "defaultValue": "[format('{0}-vnetlink', last(split(parameters('virtualNetworkResourceId'), '/')))]", - "metadata": { - "description": "Optional. The name of the virtual network link." - } - }, - "location": { - "type": "string", - "defaultValue": "global", - "metadata": { - "description": "Optional. The location of the PrivateDNSZone. Should be global." - } - }, - "tags": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Tags of the resource." - } - }, - "registrationEnabled": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "Optional. Is auto-registration of virtual machine records in the virtual network in the Private DNS zone enabled?." - } - }, - "virtualNetworkResourceId": { - "type": "string", - "metadata": { - "description": "Required. Link to another virtual network resource ID." - } - }, - "resolutionPolicy": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The resolution policy on the virtual network link. Only applicable for virtual network links to privatelink zones, and for A,AAAA,CNAME queries. When set to `NxDomainRedirect`, Azure DNS resolver falls back to public resolution if private dns query resolution results in non-existent domain response. `Default` is configured as the default option." - } - } - }, - "resources": { - "privateDnsZone": { - "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" - }, - "virtualNetworkLink": { - "type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks", - "apiVersion": "2024-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", - "properties": { - "registrationEnabled": "[parameters('registrationEnabled')]", - "virtualNetwork": { - "id": "[parameters('virtualNetworkResourceId')]" - }, - "resolutionPolicy": "[parameters('resolutionPolicy')]" - } - } - }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the deployed virtual network link." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the deployed virtual network link." - }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/virtualNetworkLinks', parameters('privateDnsZoneName'), parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group of the deployed virtual network link." - }, - "value": "[resourceGroup().name]" - }, - "location": { - "type": "string", - "metadata": { - "description": "The location the resource was deployed into." - }, - "value": "[reference('virtualNetworkLink', '2024-06-01', 'full').location]" - } - } - } + "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": [ - "privateDnsZone" + "networkSecurityGroup" ] } }, @@ -8608,30 +7416,30 @@ "resourceGroupName": { "type": "string", "metadata": { - "description": "The resource group the private DNS zone was deployed into." + "description": "The resource group the network security group was deployed into." }, "value": "[resourceGroup().name]" }, - "name": { + "resourceId": { "type": "string", "metadata": { - "description": "The name of the private DNS zone." + "description": "The resource ID of the network security group." }, - "value": "[parameters('name')]" + "value": "[resourceId('Microsoft.Network/networkSecurityGroups', parameters('name'))]" }, - "resourceId": { + "name": { "type": "string", "metadata": { - "description": "The resource ID of the private DNS zone." + "description": "The name of the network security group." }, - "value": "[resourceId('Microsoft.Network/privateDnsZones', parameters('name'))]" + "value": "[parameters('name')]" }, "location": { "type": "string", "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('privateDnsZone', '2020-06-01', 'full').location]" + "value": "[reference('networkSecurityGroup', '2023-11-01', 'full').location]" } } } @@ -8640,7 +7448,7 @@ { "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[take(format('{0}-keyvault-deployment', variables('nameFormatted')), 64)]", + "name": "[take(format('{0}-vnet', parameters('resourceToken')), 64)]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -8648,50 +7456,58 @@ "mode": "Incremental", "parameters": { "name": { - "value": "[variables('nameFormatted')]" + "value": "[format('vnet-{0}', parameters('resourceToken'))]" }, "location": { "value": "[parameters('location')]" }, - "tags": { - "value": "[parameters('tags')]" - }, - "publicNetworkAccess": "[if(parameters('networkIsolation'), createObject('value', 'Disabled'), createObject('value', 'Enabled'))]", - "networkAcls": { - "value": { - "defaultAction": "Allow" - } - }, - "enableVaultForDeployment": { - "value": true - }, - "enableVaultForDiskEncryption": { - "value": true - }, - "enableVaultForTemplateDeployment": { - "value": true - }, - "enablePurgeProtection": { - "value": true - }, - "enableRbacAuthorization": { - "value": true - }, - "enableSoftDelete": { - "value": true - }, - "softDeleteRetentionInDays": { - "value": 7 + "addressPrefixes": { + "value": [ + "10.0.0.0/8" + ] }, "diagnosticSettings": { "value": [ { - "workspaceResourceId": "[parameters('logAnalyticsWorkspaceResourceId')]" + "workspaceResourceId": "[parameters('logAnalyticsWorkspaceId')]", + "logCategoriesAndGroups": [ + { + "category": "VMProtectionAlerts" + } + ], + "metricCategories": [ + { + "category": "AllMetrics" + } + ] + } + ] + }, + "subnets": { + "value": [ + { + "name": "[variables('defaultSubnetName')]", + "addressPrefix": "10.3.1.0/24", + "privateEndpointNetworkPolicies": "Disabled", + "privateLinkServiceNetworkPolicies": "Disabled", + "networkSecurityGroupResourceId": "[reference(resourceId('Microsoft.Resources/deployments', take(format('{0}-default-nsg', parameters('resourceToken')), 64)), '2022-09-01').outputs.resourceId.value]" + }, + { + "name": "[variables('bastionSubnetName')]", + "addressPrefix": "10.3.2.0/24", + "networkSecurityGroupResourceId": "[reference(resourceId('Microsoft.Resources/deployments', take(format('{0}-bastion-nsg', parameters('resourceToken')), 64)), '2022-09-01').outputs.resourceId.value]" + }, + { + "name": "[variables('appSubnetName')]", + "addressPrefix": "10.3.3.0/24", + "networkSecurityGroupResourceId": "[reference(resourceId('Microsoft.Resources/deployments', take(format('{0}-app-nsg', parameters('resourceToken')), 64)), '2022-09-01').outputs.resourceId.value]", + "delegation": "Microsoft.Web/serverfarms" } ] }, - "privateEndpoints": "[if(parameters('networkIsolation'), createObject('value', createArray(createObject('privateDnsZoneGroup', createObject('privateDnsZoneGroupConfigs', createArray(createObject('privateDnsZoneResourceId', reference(resourceId('Microsoft.Resources/deployments', 'private-dns-keyvault-deployment'), '2022-09-01').outputs.resourceId.value))), 'service', 'vault', 'subnetResourceId', parameters('virtualNetworkSubnetResourceId')))), createObject('value', createArray()))]", - "roleAssignments": "[if(empty(parameters('userObjectId')), createObject('value', createArray()), createObject('value', createArray(createObject('principalId', parameters('userObjectId'), 'principalType', 'User', 'roleDefinitionIdOrName', 'Key Vault Secrets User'))))]" + "tags": { + "value": "[parameters('tags')]" + } }, "template": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", @@ -8700,440 +7516,229 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.92.45157", - "templateHash": "11991444046732273373" + "version": "0.35.1.17967", + "templateHash": "16195883788906927531" }, - "name": "Key Vaults", - "description": "This module deploys a Key Vault.", - "owner": "Azure/module-maintainers" + "name": "Virtual Networks", + "description": "This module deploys a Virtual Network (vNet)." }, "definitions": { - "privateEndpointOutputType": { + "peeringType": { "type": "object", "properties": { "name": { "type": "string", + "nullable": true, "metadata": { - "description": "The name of the private endpoint." + "description": "Optional. The Name of VNET Peering resource. If not provided, default value will be peer-localVnetName-remoteVnetName." } }, - "resourceId": { + "remoteVirtualNetworkResourceId": { "type": "string", "metadata": { - "description": "The resource ID of the private endpoint." + "description": "Required. The Resource ID of the VNet that is this Local VNet is being peered to. Should be in the format of a Resource ID." } }, - "groupId": { - "type": "string", + "allowForwardedTraffic": { + "type": "bool", "nullable": true, "metadata": { - "description": "The group Id for the private endpoint Group." + "description": "Optional. Whether the forwarded traffic from the VMs in the local virtual network will be allowed/disallowed in remote virtual network. Default is true." } }, - "customDnsConfigs": { - "type": "array", - "items": { - "type": "object", - "properties": { - "fqdn": { - "type": "string", - "nullable": true, - "metadata": { - "description": "FQDN that resolves to private endpoint IP address." - } - }, - "ipAddresses": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "A list of private IP addresses of the private endpoint." - } - } - } - }, + "allowGatewayTransit": { + "type": "bool", + "nullable": true, "metadata": { - "description": "The custom DNS configurations of the private endpoint." + "description": "Optional. If gateway links can be used in remote virtual networking to link to this virtual network. Default is false." } }, - "networkInterfaceResourceIds": { - "type": "array", - "items": { - "type": "string" - }, + "allowVirtualNetworkAccess": { + "type": "bool", + "nullable": true, "metadata": { - "description": "The IDs of the network interfaces associated with the private endpoint." + "description": "Optional. Whether the VMs in the local virtual network space would be able to access the VMs in remote virtual network space. Default is true." } - } - }, - "metadata": { - "__bicep_export!": true - } - }, - "credentialOutputType": { - "type": "object", - "properties": { - "resourceId": { - "type": "string", + }, + "doNotVerifyRemoteGateways": { + "type": "bool", + "nullable": true, "metadata": { - "description": "The item's resourceId." + "description": "Optional. Do not verify the provisioning state of the remote gateway. Default is true." } }, - "uri": { - "type": "string", + "useRemoteGateways": { + "type": "bool", + "nullable": true, "metadata": { - "description": "The item's uri." + "description": "Optional. If remote gateways can be used on this virtual network. If the flag is set to true, and allowGatewayTransit on remote peering is also true, virtual network will use gateways of remote virtual network for transit. Only one peering can have this flag set to true. This flag cannot be set if virtual network already has a gateway. Default is false." } }, - "uriWithVersion": { - "type": "string", + "remotePeeringEnabled": { + "type": "bool", + "nullable": true, "metadata": { - "description": "The item's uri with version." + "description": "Optional. Deploy the outbound and the inbound peering." } - } - }, - "metadata": { - "__bicep_export!": true - } - }, - "accessPolicyType": { - "type": "object", - "properties": { - "tenantId": { + }, + "remotePeeringName": { "type": "string", "nullable": true, "metadata": { - "description": "Optional. The tenant ID that is used for authenticating requests to the key vault." + "description": "Optional. The name of the VNET Peering resource in the remove Virtual Network. If not provided, default value will be peer-remoteVnetName-localVnetName." } }, - "objectId": { - "type": "string", + "remotePeeringAllowForwardedTraffic": { + "type": "bool", + "nullable": true, "metadata": { - "description": "Required. The object ID of a user, service principal or security group in the tenant for the vault." + "description": "Optional. Whether the forwarded traffic from the VMs in the local virtual network will be allowed/disallowed in remote virtual network. Default is true." } }, - "applicationId": { - "type": "string", + "remotePeeringAllowGatewayTransit": { + "type": "bool", "nullable": true, "metadata": { - "description": "Optional. Application ID of the client making request on behalf of a principal." + "description": "Optional. If gateway links can be used in remote virtual networking to link to this virtual network. Default is false." } }, - "permissions": { - "type": "object", - "properties": { - "keys": { - "type": "array", - "allowedValues": [ - "all", - "backup", - "create", - "decrypt", - "delete", - "encrypt", - "get", - "getrotationpolicy", - "import", - "list", - "purge", - "recover", - "release", - "restore", - "rotate", - "setrotationpolicy", - "sign", - "unwrapKey", - "update", - "verify", - "wrapKey" - ], - "nullable": true, - "metadata": { - "description": "Optional. Permissions to keys." - } - }, - "secrets": { - "type": "array", - "allowedValues": [ - "all", - "backup", - "delete", - "get", - "list", - "purge", - "recover", - "restore", - "set" - ], - "nullable": true, - "metadata": { - "description": "Optional. Permissions to secrets." - } - }, - "certificates": { - "type": "array", - "allowedValues": [ - "all", - "backup", - "create", - "delete", - "deleteissuers", - "get", - "getissuers", - "import", - "list", - "listissuers", - "managecontacts", - "manageissuers", - "purge", - "recover", - "restore", - "setissuers", - "update" - ], - "nullable": true, - "metadata": { - "description": "Optional. Permissions to certificates." - } - }, - "storage": { - "type": "array", - "allowedValues": [ - "all", - "backup", - "delete", - "deletesas", - "get", - "getsas", - "list", - "listsas", - "purge", - "recover", - "regeneratekey", - "restore", - "set", - "setsas", - "update" - ], - "nullable": true, - "metadata": { - "description": "Optional. Permissions to storage accounts." - } - } - }, + "remotePeeringAllowVirtualNetworkAccess": { + "type": "bool", + "nullable": true, "metadata": { - "description": "Required. Permissions the identity has for keys, secrets and certificates." + "description": "Optional. Whether the VMs in the local virtual network space would be able to access the VMs in remote virtual network space. Default is true." + } + }, + "remotePeeringDoNotVerifyRemoteGateways": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Do not verify the provisioning state of the remote gateway. Default is true." + } + }, + "remotePeeringUseRemoteGateways": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. If remote gateways can be used on this virtual network. If the flag is set to true, and allowGatewayTransit on remote peering is also true, virtual network will use gateways of remote virtual network for transit. Only one peering can have this flag set to true. This flag cannot be set if virtual network already has a gateway. Default is false." } } - }, - "metadata": { - "__bicep_export!": true } }, - "secretType": { + "subnetType": { "type": "object", "properties": { "name": { "type": "string", "metadata": { - "description": "Required. The name of the secret." + "description": "Required. The Name of the subnet resource." } }, - "tags": { - "type": "object", + "addressPrefix": { + "type": "string", "nullable": true, "metadata": { - "description": "Optional. Resource tags." + "description": "Conditional. The address prefix for the subnet. Required if `addressPrefixes` is empty." } }, - "attributes": { - "type": "object", - "properties": { - "enabled": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Defines whether the secret is enabled or disabled." - } - }, - "exp": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. Defines when the secret will become invalid. Defined in seconds since 1970-01-01T00:00:00Z." - } - }, - "nbf": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. If set, defines the date from which onwards the secret becomes valid. Defined in seconds since 1970-01-01T00:00:00Z." - } - } + "addressPrefixes": { + "type": "array", + "items": { + "type": "string" }, "nullable": true, "metadata": { - "description": "Optional. Contains attributes of the secret." + "description": "Conditional. List of address prefixes for the subnet. Required if `addressPrefix` is empty." } }, - "contentType": { - "type": "string", + "ipamPoolPrefixAllocations": { + "type": "array", + "prefixItems": [ + { + "type": "object", + "properties": { + "pool": { + "type": "object", + "properties": { + "id": { + "type": "string", + "metadata": { + "description": "Required. The Resource ID of the IPAM pool." + } + } + }, + "metadata": { + "description": "Required. The Resource ID of the IPAM pool." + } + }, + "numberOfIpAddresses": { + "type": "string", + "metadata": { + "description": "Required. Number of IP addresses allocated from the pool." + } + } + } + } + ], + "items": false, "nullable": true, "metadata": { - "description": "Optional. The content type of the secret." - } - }, - "value": { - "type": "securestring", - "metadata": { - "description": "Required. The value of the secret. NOTE: \"value\" will never be returned from the service, as APIs using this model are is intended for internal use in ARM deployments. Users should use the data-plane REST service for interaction with vault secrets." + "description": "Conditional. The address space for the subnet, deployed from IPAM Pool. Required if `addressPrefixes` and `addressPrefix` is empty and the VNet address space configured to use IPAM Pool." } }, - "roleAssignments": { + "applicationGatewayIPConfigurations": { "type": "array", "items": { - "$ref": "#/definitions/roleAssignmentType" + "type": "object" }, "nullable": true, "metadata": { - "description": "Optional. Array of role assignments to create." - } - } - }, - "metadata": { - "__bicep_export!": true - } - }, - "keyType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the key." - } - }, - "tags": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Resource tags." + "description": "Optional. Application gateway IP configurations of virtual network resource." } }, - "attributes": { - "type": "object", - "properties": { - "enabled": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Defines whether the key is enabled or disabled." - } - }, - "exp": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. Defines when the key will become invalid. Defined in seconds since 1970-01-01T00:00:00Z." - } - }, - "nbf": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. If set, defines the date from which onwards the key becomes valid. Defined in seconds since 1970-01-01T00:00:00Z." - } - } - }, + "delegation": { + "type": "string", "nullable": true, "metadata": { - "description": "Optional. Contains attributes of the key." + "description": "Optional. The delegation to enable on the subnet." } }, - "curveName": { + "natGatewayResourceId": { "type": "string", - "allowedValues": [ - "P-256", - "P-256K", - "P-384", - "P-521" - ], "nullable": true, "metadata": { - "description": "Optional. The elliptic curve name. Only works if \"keySize\" equals \"EC\" or \"EC-HSM\". Default is \"P-256\"." + "description": "Optional. The resource ID of the NAT Gateway to use for the subnet." } }, - "keyOps": { - "type": "array", - "allowedValues": [ - "decrypt", - "encrypt", - "import", - "release", - "sign", - "unwrapKey", - "verify", - "wrapKey" - ], + "networkSecurityGroupResourceId": { + "type": "string", "nullable": true, "metadata": { - "description": "Optional. The allowed operations on this key." + "description": "Optional. The resource ID of the network security group to assign to the subnet." } }, - "keySize": { - "type": "int", + "privateEndpointNetworkPolicies": { + "type": "string", "allowedValues": [ - 2048, - 3072, - 4096 + "Disabled", + "Enabled", + "NetworkSecurityGroupEnabled", + "RouteTableEnabled" ], "nullable": true, "metadata": { - "description": "Optional. The key size in bits. Only works if \"keySize\" equals \"RSA\" or \"RSA-HSM\". Default is \"4096\"." + "description": "Optional. enable or disable apply network policies on private endpoint in the subnet." } }, - "kty": { + "privateLinkServiceNetworkPolicies": { "type": "string", "allowedValues": [ - "EC", - "EC-HSM", - "RSA", - "RSA-HSM" + "Disabled", + "Enabled" ], "nullable": true, "metadata": { - "description": "Optional. The type of the key. Default is \"EC\"." - } - }, - "releasePolicy": { - "type": "object", - "properties": { - "contentType": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Content type and version of key release policy." - } - }, - "data": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Blob encoding the policy rules under which the key can be released." - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. Key release policy." - } - }, - "rotationPolicy": { - "$ref": "#/definitions/rotationPolicyType", - "nullable": true, - "metadata": { - "description": "Optional. Key rotation policy." + "description": "Optional. enable or disable apply network policies on private link service in the subnet." } }, "roleAssignments": { @@ -9145,195 +7750,52 @@ "metadata": { "description": "Optional. Array of role assignments to create." } - } - }, - "metadata": { - "__bicep_export!": true - } - }, - "rotationPolicyType": { - "type": "object", - "properties": { - "attributes": { - "type": "object", - "properties": { - "expiryTime": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The expiration time for the new key version. It should be in ISO8601 format. Eg: \"P90D\", \"P1Y\"." - } - } - }, + }, + "routeTableResourceId": { + "type": "string", "nullable": true, "metadata": { - "description": "Optional. The attributes of key rotation policy." + "description": "Optional. The resource ID of the route table to assign to the subnet." } }, - "lifetimeActions": { + "serviceEndpointPolicies": { "type": "array", "items": { - "type": "object", - "properties": { - "action": { - "type": "object", - "properties": { - "type": { - "type": "string", - "allowedValues": [ - "Notify", - "Rotate" - ], - "nullable": true, - "metadata": { - "description": "Optional. The type of action." - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. The action of key rotation policy lifetimeAction." - } - }, - "trigger": { - "type": "object", - "properties": { - "timeAfterCreate": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The time duration after key creation to rotate the key. It only applies to rotate. It will be in ISO 8601 duration format. Eg: \"P90D\", \"P1Y\"." - } - }, - "timeBeforeExpiry": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The time duration before key expiring to rotate or notify. It will be in ISO 8601 duration format. Eg: \"P90D\", \"P1Y\"." - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. The trigger of key rotation policy lifetimeAction." - } - } - } + "type": "object" }, "nullable": true, "metadata": { - "description": "Optional. The lifetimeActions for key rotation action." - } - } - } - }, - "_1.privateEndpointCustomDnsConfigType": { - "type": "object", - "properties": { - "fqdn": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. FQDN that resolves to private endpoint IP address." + "description": "Optional. An array of service endpoint policies." } }, - "ipAddresses": { + "serviceEndpoints": { "type": "array", "items": { "type": "string" }, + "nullable": true, "metadata": { - "description": "Required. A list of private IP addresses of the private endpoint." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.2.1" - } - } - }, - "_1.privateEndpointIpConfigurationType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the resource that is unique within a resource group." + "description": "Optional. The service endpoints to enable on the subnet." } }, - "properties": { - "type": "object", - "properties": { - "groupId": { - "type": "string", - "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." - } - }, - "memberName": { - "type": "string", - "metadata": { - "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to." - } - }, - "privateIPAddress": { - "type": "string", - "metadata": { - "description": "Required. A private IP address obtained from the private endpoint's subnet." - } - } - }, - "metadata": { - "description": "Required. Properties of private endpoint IP configurations." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.2.1" - } - } - }, - "_1.privateEndpointPrivateDnsZoneGroupType": { - "type": "object", - "properties": { - "name": { - "type": "string", + "defaultOutboundAccess": { + "type": "bool", "nullable": true, "metadata": { - "description": "Optional. The name of the Private DNS Zone Group." + "description": "Optional. Set this property to false to disable default outbound connectivity for all VMs in the subnet. This property can only be set at the time of subnet creation and cannot be updated for an existing subnet." } }, - "privateDnsZoneGroupConfigs": { - "type": "array", - "items": { - "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." - } - } - } - }, + "sharingScope": { + "type": "string", + "allowedValues": [ + "DelegatedServices", + "Tenant" + ], + "nullable": true, "metadata": { - "description": "Required. The private DNS Zone Groups to associate the Private Endpoint. A DNS Zone Group can support up to 5 DNS zones." + "description": "Optional. Set this property to Tenant to allow sharing subnet with other subscriptions in your AAD tenant. This property can only be set if defaultOutboundAccess is set to false, both properties can only be set if subnet is empty." } } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.2.1" - } } }, "diagnosticSettingFullType": { @@ -9488,196 +7950,54 @@ } } }, - "privateEndpointSingleServiceType": { + "roleAssignmentType": { "type": "object", "properties": { "name": { "type": "string", "nullable": true, "metadata": { - "description": "Optional. The name of the Private Endpoint." + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." } }, - "location": { + "roleDefinitionIdOrName": { "type": "string", - "nullable": true, "metadata": { - "description": "Optional. The location to deploy the Private Endpoint to." + "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'." } }, - "privateLinkServiceConnectionName": { + "principalId": { "type": "string", - "nullable": true, "metadata": { - "description": "Optional. The name of the private link connection to create." + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." } }, - "service": { + "principalType": { "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], "nullable": true, "metadata": { - "description": "Optional. The subresource to deploy the Private Endpoint for. For example \"vault\" for a Key Vault Private Endpoint." + "description": "Optional. The principal type of the assigned principal ID." } }, - "subnetResourceId": { + "description": { "type": "string", - "metadata": { - "description": "Required. Resource ID of the subnet where the endpoint needs to be created." - } - }, - "privateDnsZoneGroup": { - "$ref": "#/definitions/_1.privateEndpointPrivateDnsZoneGroupType", "nullable": true, "metadata": { - "description": "Optional. The private DNS Zone Group to configure for the Private Endpoint." + "description": "Optional. The description of the role assignment." } }, - "isManualConnection": { - "type": "bool", + "condition": { + "type": "string", "nullable": true, "metadata": { - "description": "Optional. If Manual Private Link Connection is required." - } - }, - "manualConnectionRequestMessage": { - "type": "string", - "nullable": true, - "maxLength": 140, - "metadata": { - "description": "Optional. A message passed to the owner of the remote resource with the manual connection request." - } - }, - "customDnsConfigs": { - "type": "array", - "items": { - "$ref": "#/definitions/_1.privateEndpointCustomDnsConfigType" - }, - "nullable": true, - "metadata": { - "description": "Optional. Custom DNS configurations." - } - }, - "ipConfigurations": { - "type": "array", - "items": { - "$ref": "#/definitions/_1.privateEndpointIpConfigurationType" - }, - "nullable": true, - "metadata": { - "description": "Optional. A list of IP configurations of the Private Endpoint. This will be used to map to the first-party Service endpoints." - } - }, - "applicationSecurityGroupResourceIds": { - "type": "array", - "items": { - "type": "string" - }, - "nullable": true, - "metadata": { - "description": "Optional. Application security groups in which the Private Endpoint IP configuration is included." - } - }, - "customNetworkInterfaceName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The custom name of the network interface attached to the Private Endpoint." - } - }, - "lock": { - "$ref": "#/definitions/lockType", - "nullable": true, - "metadata": { - "description": "Optional. Specify the type of lock." - } - }, - "roleAssignments": { - "type": "array", - "items": { - "$ref": "#/definitions/roleAssignmentType" - }, - "nullable": true, - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "tags": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Tags to be applied on all resources/Resource Groups in this deployment." - } - }, - "enableTelemetry": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Enable/Disable usage telemetry for module." - } - }, - "resourceGroupName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Specify if you want to deploy the Private Endpoint into a different Resource Group than the main resource." - } - } - }, - "metadata": { - "description": "An AVM-aligned type for a private endpoint. To be used if the private endpoint's default service / groupId can be assumed (i.e., for services that only have one Private Endpoint type like 'vault' for key vault).", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.2.1" - } - } - }, - "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\"." + "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": { @@ -9709,9 +8029,8 @@ "parameters": { "name": { "type": "string", - "maxLength": 24, "metadata": { - "description": "Required. Name of the Key Vault. Must be globally unique." + "description": "Required. The name of the Virtual Network (vNet)." } }, "location": { @@ -9721,120 +8040,97 @@ "description": "Optional. Location for all resources." } }, - "accessPolicies": { + "addressPrefixes": { "type": "array", - "items": { - "$ref": "#/definitions/accessPolicyType" - }, + "metadata": { + "description": "Required. An Array of 1 or more IP Address Prefixes OR the resource ID of the IPAM pool to be used for the Virtual Network. When specifying an IPAM pool resource ID you must also set a value for the parameter called `ipamPoolNumberOfIpAddresses`." + } + }, + "ipamPoolNumberOfIpAddresses": { + "type": "string", "nullable": true, "metadata": { - "description": "Optional. All access policies to create." + "description": "Optional. Number of IP addresses allocated from the pool. To be used only when the addressPrefix param is defined with a resource ID of an IPAM pool." } }, - "secrets": { - "type": "array", - "items": { - "$ref": "#/definitions/secretType" - }, + "virtualNetworkBgpCommunity": { + "type": "string", "nullable": true, "metadata": { - "description": "Optional. All secrets to create." + "description": "Optional. The BGP community associated with the virtual network." } }, - "keys": { + "subnets": { "type": "array", "items": { - "$ref": "#/definitions/keyType" + "$ref": "#/definitions/subnetType" }, "nullable": true, "metadata": { - "description": "Optional. All keys to create." - } - }, - "enableVaultForDeployment": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Optional. Specifies if the vault is enabled for deployment by script or compute." - } - }, - "enableVaultForTemplateDeployment": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Optional. Specifies if the vault is enabled for a template deployment." - } - }, - "enableVaultForDiskEncryption": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Optional. Specifies if the azure platform has access to the vault for enabling disk encryption scenarios." - } - }, - "enableSoftDelete": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Optional. Switch to enable/disable Key Vault's soft delete feature." + "description": "Optional. An Array of subnets to deploy to the Virtual Network." } }, - "softDeleteRetentionInDays": { - "type": "int", - "defaultValue": 90, + "dnsServers": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, "metadata": { - "description": "Optional. softDelete data retention days. It accepts >=7 and <=90." + "description": "Optional. DNS Servers associated to the Virtual Network." } }, - "enableRbacAuthorization": { - "type": "bool", - "defaultValue": true, + "ddosProtectionPlanResourceId": { + "type": "string", + "nullable": true, "metadata": { - "description": "Optional. Property that controls how data actions are authorized. When true, the key vault will use Role Based Access Control (RBAC) for authorization of data actions, and the access policies specified in vault properties will be ignored. When false, the key vault will use the access policies specified in vault properties, and any policy stored on Azure Resource Manager will be ignored. Note that management actions are always authorized with RBAC." + "description": "Optional. Resource ID of the DDoS protection plan to assign the VNET to. If it's left blank, DDoS protection will not be configured. If it's provided, the VNET created by this template will be attached to the referenced DDoS protection plan. The DDoS protection plan can exist in the same or in a different subscription." } }, - "createMode": { - "type": "string", - "defaultValue": "default", + "peerings": { + "type": "array", + "items": { + "$ref": "#/definitions/peeringType" + }, + "nullable": true, "metadata": { - "description": "Optional. The vault's create mode to indicate whether the vault need to be recovered or not. - recover or default." + "description": "Optional. Virtual Network Peering configurations." } }, - "enablePurgeProtection": { + "vnetEncryption": { "type": "bool", - "defaultValue": true, + "defaultValue": false, "metadata": { - "description": "Optional. Provide 'true' to enable Key Vault's purge protection feature." + "description": "Optional. Indicates if encryption is enabled on virtual network and if VM without encryption is allowed in encrypted VNet. Requires the EnableVNetEncryption feature to be registered for the subscription and a supported region to use this property." } }, - "sku": { + "vnetEncryptionEnforcement": { "type": "string", - "defaultValue": "premium", + "defaultValue": "AllowUnencrypted", "allowedValues": [ - "premium", - "standard" + "AllowUnencrypted", + "DropUnencrypted" ], "metadata": { - "description": "Optional. Specifies the SKU for the vault." + "description": "Optional. If the encrypted VNet allows VM that does not support encryption. Can only be used when vnetEncryption is enabled." } }, - "networkAcls": { - "type": "object", - "nullable": true, + "flowTimeoutInMinutes": { + "type": "int", + "defaultValue": 0, + "maxValue": 30, "metadata": { - "description": "Optional. Rules governing the accessibility of the resource from specific network locations." + "description": "Optional. The flow timeout in minutes for the Virtual Network, which is used to enable connection tracking for intra-VM flows. Possible values are between 4 and 30 minutes. Default value 0 will set the property to null." } }, - "publicNetworkAccess": { - "type": "string", - "defaultValue": "", - "allowedValues": [ - "", - "Enabled", - "Disabled" - ], + "diagnosticSettings": { + "type": "array", + "items": { + "$ref": "#/definitions/diagnosticSettingFullType" + }, + "nullable": true, "metadata": { - "description": "Optional. Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set and networkAcls are not set." + "description": "Optional. The diagnostic settings of the service." } }, "lock": { @@ -9854,31 +8150,11 @@ "description": "Optional. Array of role assignments to create." } }, - "privateEndpoints": { - "type": "array", - "items": { - "$ref": "#/definitions/privateEndpointSingleServiceType" - }, - "nullable": true, - "metadata": { - "description": "Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible." - } - }, "tags": { "type": "object", "nullable": true, "metadata": { - "description": "Optional. Resource tags." - } - }, - "diagnosticSettings": { - "type": "array", - "items": { - "$ref": "#/definitions/diagnosticSettingFullType" - }, - "nullable": true, - "metadata": { - "description": "Optional. The diagnostic settings of the service." + "description": "Optional. Tags of the resource." } }, "enableTelemetry": { @@ -9887,6 +8163,13 @@ "metadata": { "description": "Optional. Enable/Disable usage telemetry for module." } + }, + "enableVmProtection": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Indicates if VM protection is enabled for all the subnets in the virtual network." + } } }, "variables": { @@ -9895,30 +8178,12 @@ "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)))))]" - }, - { - "name": "formattedAccessPolicies", - "count": "[length(coalesce(parameters('accessPolicies'), createArray()))]", - "input": { - "applicationId": "[coalesce(tryGet(coalesce(parameters('accessPolicies'), createArray())[copyIndex('formattedAccessPolicies')], 'applicationId'), '')]", - "objectId": "[coalesce(parameters('accessPolicies'), createArray())[copyIndex('formattedAccessPolicies')].objectId]", - "permissions": "[coalesce(parameters('accessPolicies'), createArray())[copyIndex('formattedAccessPolicies')].permissions]", - "tenantId": "[coalesce(tryGet(coalesce(parameters('accessPolicies'), createArray())[copyIndex('formattedAccessPolicies')], 'tenantId'), tenant().tenantId)]" - } } ], + "enableReferencedModulesTelemetry": false, "builtInRoleNames": { "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", - "Key Vault Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00482a5a-887f-4fb3-b363-3b7fe8e74483')]", - "Key Vault Certificates Officer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4417e6f-fecd-4de8-b567-7b0420556985')]", - "Key Vault Certificate User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'db79e9a7-68ee-4b58-9aeb-b90e7c24fcba')]", - "Key Vault Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f25e0fa2-a7c8-4377-a976-54943a77a395')]", - "Key Vault Crypto Officer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '14b46e9e-c2b7-41b4-b07b-48a6ebf60603')]", - "Key Vault Crypto Service Encryption User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e147488a-f6f5-4113-8e2d-b22465e65bf6')]", - "Key Vault Crypto User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '12338af0-0e69-4776-bea7-57ae8d297424')]", - "Key Vault Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21090545-7ca7-4776-b22c-e363652d74d2')]", - "Key Vault Secrets Officer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b86a8fe4-44ce-4948-aee5-eccb2c155cd7')]", - "Key Vault Secrets User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4633458b-17de-408a-b874-0445c86b69e6')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", @@ -9930,7 +8195,7 @@ "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2024-03-01", - "name": "[format('46d3xbcp.res.keyvault-vault.{0}.{1}', replace('0.11.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "name": "[format('46d3xbcp.res.network-virtualnetwork.{0}.{1}', replace('0.7.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -9946,53 +8211,45 @@ } } }, - "keyVault": { - "type": "Microsoft.KeyVault/vaults", - "apiVersion": "2022-07-01", + "virtualNetwork": { + "type": "Microsoft.Network/virtualNetworks", + "apiVersion": "2024-05-01", "name": "[parameters('name')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", "properties": { - "enabledForDeployment": "[parameters('enableVaultForDeployment')]", - "enabledForTemplateDeployment": "[parameters('enableVaultForTemplateDeployment')]", - "enabledForDiskEncryption": "[parameters('enableVaultForDiskEncryption')]", - "enableSoftDelete": "[parameters('enableSoftDelete')]", - "softDeleteRetentionInDays": "[parameters('softDeleteRetentionInDays')]", - "enableRbacAuthorization": "[parameters('enableRbacAuthorization')]", - "createMode": "[parameters('createMode')]", - "enablePurgeProtection": "[if(parameters('enablePurgeProtection'), parameters('enablePurgeProtection'), null())]", - "tenantId": "[subscription().tenantId]", - "accessPolicies": "[variables('formattedAccessPolicies')]", - "sku": { - "name": "[parameters('sku')]", - "family": "A" - }, - "networkAcls": "[if(not(empty(coalesce(parameters('networkAcls'), createObject()))), createObject('bypass', tryGet(parameters('networkAcls'), 'bypass'), 'defaultAction', tryGet(parameters('networkAcls'), 'defaultAction'), 'virtualNetworkRules', coalesce(tryGet(parameters('networkAcls'), 'virtualNetworkRules'), createArray()), 'ipRules', coalesce(tryGet(parameters('networkAcls'), 'ipRules'), createArray())), null())]", - "publicNetworkAccess": "[if(not(empty(parameters('publicNetworkAccess'))), parameters('publicNetworkAccess'), if(and(not(empty(coalesce(parameters('privateEndpoints'), createArray()))), empty(coalesce(parameters('networkAcls'), createObject()))), 'Disabled', null()))]" + "addressSpace": "[if(contains(parameters('addressPrefixes')[0], '/Microsoft.Network/networkManagers/'), createObject('ipamPoolPrefixAllocations', createArray(createObject('pool', createObject('id', parameters('addressPrefixes')[0]), 'numberOfIpAddresses', parameters('ipamPoolNumberOfIpAddresses')))), createObject('addressPrefixes', parameters('addressPrefixes')))]", + "bgpCommunities": "[if(not(empty(parameters('virtualNetworkBgpCommunity'))), createObject('virtualNetworkCommunity', parameters('virtualNetworkBgpCommunity')), null())]", + "ddosProtectionPlan": "[if(not(empty(parameters('ddosProtectionPlanResourceId'))), createObject('id', parameters('ddosProtectionPlanResourceId')), null())]", + "dhcpOptions": "[if(not(empty(parameters('dnsServers'))), createObject('dnsServers', array(parameters('dnsServers'))), null())]", + "enableDdosProtection": "[not(empty(parameters('ddosProtectionPlanResourceId')))]", + "encryption": "[if(equals(parameters('vnetEncryption'), true()), createObject('enabled', parameters('vnetEncryption'), 'enforcement', parameters('vnetEncryptionEnforcement')), null())]", + "flowTimeoutInMinutes": "[if(not(equals(parameters('flowTimeoutInMinutes'), 0)), parameters('flowTimeoutInMinutes'), null())]", + "enableVmProtection": "[parameters('enableVmProtection')]" } }, - "keyVault_lock": { + "virtualNetwork_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.KeyVault/vaults/{0}', parameters('name'))]", + "scope": "[format('Microsoft.Network/virtualNetworks/{0}', parameters('name'))]", "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", "properties": { "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" }, "dependsOn": [ - "keyVault" + "virtualNetwork" ] }, - "keyVault_diagnosticSettings": { + "virtualNetwork_diagnosticSettings": { "copy": { - "name": "keyVault_diagnosticSettings", + "name": "virtualNetwork_diagnosticSettings", "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" }, "type": "Microsoft.Insights/diagnosticSettings", "apiVersion": "2021-05-01-preview", - "scope": "[format('Microsoft.KeyVault/vaults/{0}', parameters('name'))]", + "scope": "[format('Microsoft.Network/virtualNetworks/{0}', parameters('name'))]", "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", "properties": { "copy": [ @@ -10023,18 +8280,18 @@ "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" }, "dependsOn": [ - "keyVault" + "virtualNetwork" ] }, - "keyVault_roleAssignments": { + "virtualNetwork_roleAssignments": { "copy": { - "name": "keyVault_roleAssignments", + "name": "virtualNetwork_roleAssignments", "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.KeyVault/vaults/{0}', parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.KeyVault/vaults', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "scope": "[format('Microsoft.Network/virtualNetworks/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/virtualNetworks', 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]", @@ -10045,294 +8302,78 @@ "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" }, "dependsOn": [ - "keyVault" + "virtualNetwork" ] }, - "keyVault_accessPolicies": { - "condition": "[not(empty(parameters('accessPolicies')))]", + "virtualNetwork_subnets": { + "copy": { + "name": "virtualNetwork_subnets", + "count": "[length(coalesce(parameters('subnets'), createArray()))]", + "mode": "serial", + "batchSize": 1 + }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-KeyVault-AccessPolicies', uniqueString(deployment().name, parameters('location')))]", + "name": "[format('{0}-subnet-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "keyVaultName": { + "virtualNetworkName": { "value": "[parameters('name')]" }, - "accessPolicies": { - "value": "[parameters('accessPolicies')]" - } - }, - "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.31.92.45157", - "templateHash": "2943121976508120416" - }, - "name": "Key Vault Access Policies", - "description": "This module deploys a Key Vault Access Policy.", - "owner": "Azure/module-maintainers" + "name": { + "value": "[coalesce(parameters('subnets'), createArray())[copyIndex()].name]" }, - "definitions": { - "accessPoliciesType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "tenantId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The tenant ID that is used for authenticating requests to the key vault." - } - }, - "objectId": { - "type": "string", - "metadata": { - "description": "Required. The object ID of a user, service principal or security group in the tenant for the vault." - } - }, - "applicationId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Application ID of the client making request on behalf of a principal." - } - }, - "permissions": { - "type": "object", - "properties": { - "keys": { - "type": "array", - "allowedValues": [ - "all", - "backup", - "create", - "decrypt", - "delete", - "encrypt", - "get", - "getrotationpolicy", - "import", - "list", - "purge", - "recover", - "release", - "restore", - "rotate", - "setrotationpolicy", - "sign", - "unwrapKey", - "update", - "verify", - "wrapKey" - ], - "nullable": true, - "metadata": { - "description": "Optional. Permissions to keys." - } - }, - "secrets": { - "type": "array", - "allowedValues": [ - "all", - "backup", - "delete", - "get", - "list", - "purge", - "recover", - "restore", - "set" - ], - "nullable": true, - "metadata": { - "description": "Optional. Permissions to secrets." - } - }, - "certificates": { - "type": "array", - "allowedValues": [ - "all", - "backup", - "create", - "delete", - "deleteissuers", - "get", - "getissuers", - "import", - "list", - "listissuers", - "managecontacts", - "manageissuers", - "purge", - "recover", - "restore", - "setissuers", - "update" - ], - "nullable": true, - "metadata": { - "description": "Optional. Permissions to certificates." - } - }, - "storage": { - "type": "array", - "allowedValues": [ - "all", - "backup", - "delete", - "deletesas", - "get", - "getsas", - "list", - "listsas", - "purge", - "recover", - "regeneratekey", - "restore", - "set", - "setsas", - "update" - ], - "nullable": true, - "metadata": { - "description": "Optional. Permissions to storage accounts." - } - } - }, - "metadata": { - "description": "Required. Permissions the identity has for keys, secrets and certificates." - } - } - } - }, - "nullable": true - } + "addressPrefix": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'addressPrefix')]" }, - "parameters": { - "keyVaultName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent key vault. Required if the template is used in a standalone deployment." - } - }, - "accessPolicies": { - "$ref": "#/definitions/accessPoliciesType", - "metadata": { - "description": "Optional. An array of 0 to 16 identities that have access to the key vault. All identities in the array must use the same tenant ID as the key vault's tenant ID." - } - } + "addressPrefixes": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'addressPrefixes')]" }, - "variables": { - "copy": [ - { - "name": "formattedAccessPolicies", - "count": "[length(coalesce(parameters('accessPolicies'), createArray()))]", - "input": { - "applicationId": "[coalesce(tryGet(coalesce(parameters('accessPolicies'), createArray())[copyIndex('formattedAccessPolicies')], 'applicationId'), '')]", - "objectId": "[coalesce(parameters('accessPolicies'), createArray())[copyIndex('formattedAccessPolicies')].objectId]", - "permissions": "[coalesce(parameters('accessPolicies'), createArray())[copyIndex('formattedAccessPolicies')].permissions]", - "tenantId": "[coalesce(tryGet(coalesce(parameters('accessPolicies'), createArray())[copyIndex('formattedAccessPolicies')], 'tenantId'), tenant().tenantId)]" - } - } - ] + "ipamPoolPrefixAllocations": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'ipamPoolPrefixAllocations')]" }, - "resources": { - "keyVault": { - "existing": true, - "type": "Microsoft.KeyVault/vaults", - "apiVersion": "2022-07-01", - "name": "[parameters('keyVaultName')]" - }, - "policies": { - "type": "Microsoft.KeyVault/vaults/accessPolicies", - "apiVersion": "2022-07-01", - "name": "[format('{0}/{1}', parameters('keyVaultName'), 'add')]", - "properties": { - "accessPolicies": "[variables('formattedAccessPolicies')]" - }, - "dependsOn": [ - "keyVault" - ] - } + "applicationGatewayIPConfigurations": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'applicationGatewayIPConfigurations')]" }, - "outputs": { - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The name of the resource group the access policies assignment was created in." - }, - "value": "[resourceGroup().name]" - }, - "name": { - "type": "string", - "metadata": { - "description": "The name of the access policies assignment." - }, - "value": "add" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the access policies assignment." - }, - "value": "[resourceId('Microsoft.KeyVault/vaults/accessPolicies', parameters('keyVaultName'), 'add')]" - } - } - } - }, - "dependsOn": [ - "keyVault" - ] - }, - "keyVault_secrets": { - "copy": { - "name": "keyVault_secrets", - "count": "[length(coalesce(parameters('secrets'), createArray()))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-KeyVault-Secret-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[coalesce(parameters('secrets'), createArray())[copyIndex()].name]" + "delegation": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'delegation')]" }, - "value": { - "value": "[coalesce(parameters('secrets'), createArray())[copyIndex()].value]" + "natGatewayResourceId": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'natGatewayResourceId')]" }, - "keyVaultName": { - "value": "[parameters('name')]" + "networkSecurityGroupResourceId": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'networkSecurityGroupResourceId')]" }, - "attributesEnabled": { - "value": "[tryGet(tryGet(coalesce(parameters('secrets'), createArray())[copyIndex()], 'attributes'), 'enabled')]" + "privateEndpointNetworkPolicies": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'privateEndpointNetworkPolicies')]" }, - "attributesExp": { - "value": "[tryGet(tryGet(coalesce(parameters('secrets'), createArray())[copyIndex()], 'attributes'), 'exp')]" + "privateLinkServiceNetworkPolicies": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'privateLinkServiceNetworkPolicies')]" }, - "attributesNbf": { - "value": "[tryGet(tryGet(coalesce(parameters('secrets'), createArray())[copyIndex()], 'attributes'), 'nbf')]" + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'roleAssignments')]" }, - "contentType": { - "value": "[tryGet(coalesce(parameters('secrets'), createArray())[copyIndex()], 'contentType')]" + "routeTableResourceId": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'routeTableResourceId')]" }, - "tags": { - "value": "[coalesce(tryGet(coalesce(parameters('secrets'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + "serviceEndpointPolicies": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'serviceEndpointPolicies')]" }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('secrets'), createArray())[copyIndex()], 'roleAssignments')]" + "serviceEndpoints": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'serviceEndpoints')]" + }, + "defaultOutboundAccess": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'defaultOutboundAccess')]" + }, + "sharingScope": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'sharingScope')]" + }, + "enableTelemetry": { + "value": "[variables('enableReferencedModulesTelemetry')]" } }, "template": { @@ -10342,12 +8383,11 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.92.45157", - "templateHash": "15057494123851631617" + "version": "0.35.1.17967", + "templateHash": "9728353654559466189" }, - "name": "Key Vault Secrets", - "description": "This module deploys a Key Vault Secret.", - "owner": "Azure/module-maintainers" + "name": "Virtual Network Subnets", + "description": "This module deploys a Virtual Network Subnet." }, "definitions": { "roleAssignmentType": { @@ -10427,57 +8467,137 @@ } }, "parameters": { - "keyVaultName": { + "name": { "type": "string", "metadata": { - "description": "Conditional. The name of the parent key vault. Required if the template is used in a standalone deployment." + "description": "Required. The Name of the subnet resource." } }, - "name": { + "virtualNetworkName": { "type": "string", "metadata": { - "description": "Required. The name of the secret." + "description": "Conditional. The name of the parent virtual network. Required if the template is used in a standalone deployment." } }, - "tags": { - "type": "object", + "addressPrefix": { + "type": "string", "nullable": true, "metadata": { - "description": "Optional. Resource tags." + "description": "Conditional. The address prefix for the subnet. Required if `addressPrefixes` is empty." } }, - "attributesEnabled": { - "type": "bool", - "defaultValue": true, + "ipamPoolPrefixAllocations": { + "type": "array", + "items": { + "type": "object" + }, + "nullable": true, "metadata": { - "description": "Optional. Determines whether the object is enabled." + "description": "Conditional. The address space for the subnet, deployed from IPAM Pool. Required if `addressPrefixes` and `addressPrefix` is empty." } }, - "attributesExp": { - "type": "int", + "networkSecurityGroupResourceId": { + "type": "string", "nullable": true, "metadata": { - "description": "Optional. Expiry date in seconds since 1970-01-01T00:00:00Z. For security reasons, it is recommended to set an expiration date whenever possible." + "description": "Optional. The resource ID of the network security group to assign to the subnet." } }, - "attributesNbf": { - "type": "int", + "routeTableResourceId": { + "type": "string", "nullable": true, "metadata": { - "description": "Optional. Not before date in seconds since 1970-01-01T00:00:00Z." + "description": "Optional. The resource ID of the route table to assign to the subnet." } }, - "contentType": { - "type": "securestring", + "serviceEndpoints": { + "type": "array", + "items": { + "type": "string" + }, + "defaultValue": [], + "metadata": { + "description": "Optional. The service endpoints to enable on the subnet." + } + }, + "delegation": { + "type": "string", "nullable": true, "metadata": { - "description": "Optional. The content type of the secret." + "description": "Optional. The delegation to enable on the subnet." } }, - "value": { - "type": "securestring", + "natGatewayResourceId": { + "type": "string", + "nullable": true, "metadata": { - "description": "Required. The value of the secret. NOTE: \"value\" will never be returned from the service, as APIs using this model are is intended for internal use in ARM deployments. Users should use the data-plane REST service for interaction with vault secrets." + "description": "Optional. The resource ID of the NAT Gateway to use for the subnet." + } + }, + "privateEndpointNetworkPolicies": { + "type": "string", + "nullable": true, + "allowedValues": [ + "Disabled", + "Enabled", + "NetworkSecurityGroupEnabled", + "RouteTableEnabled" + ], + "metadata": { + "description": "Optional. Enable or disable apply network policies on private endpoint in the subnet." + } + }, + "privateLinkServiceNetworkPolicies": { + "type": "string", + "nullable": true, + "allowedValues": [ + "Disabled", + "Enabled" + ], + "metadata": { + "description": "Optional. Enable or disable apply network policies on private link service in the subnet." + } + }, + "addressPrefixes": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Conditional. List of address prefixes for the subnet. Required if `addressPrefix` is empty." + } + }, + "defaultOutboundAccess": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Set this property to false to disable default outbound connectivity for all VMs in the subnet. This property can only be set at the time of subnet creation and cannot be updated for an existing subnet." + } + }, + "sharingScope": { + "type": "string", + "allowedValues": [ + "DelegatedServices", + "Tenant" + ], + "nullable": true, + "metadata": { + "description": "Optional. Set this property to Tenant to allow sharing the subnet with other subscriptions in your AAD tenant. This property can only be set if defaultOutboundAccess is set to false, both properties can only be set if the subnet is empty." + } + }, + "applicationGatewayIPConfigurations": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Application gateway IP configurations of virtual network resource." + } + }, + "serviceEndpointPolicies": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. An array of service endpoint policies." } }, "roleAssignments": { @@ -10489,6 +8609,13 @@ "metadata": { "description": "Optional. Array of role assignments to create." } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } } }, "variables": { @@ -10501,11 +8628,7 @@ ], "builtInRoleNames": { "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", - "Key Vault Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00482a5a-887f-4fb3-b363-3b7fe8e74483')]", - "Key Vault Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f25e0fa2-a7c8-4377-a976-54943a77a395')]", - "Key Vault Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21090545-7ca7-4776-b22c-e363652d74d2')]", - "Key Vault Secrets Officer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b86a8fe4-44ce-4948-aee5-eccb2c155cd7')]", - "Key Vault Secrets User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4633458b-17de-408a-b874-0445c86b69e6')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", @@ -10513,39 +8636,70 @@ } }, "resources": { - "keyVault": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-virtualnetworksubnet.{0}.{1}', replace('0.1.2', '.', '-'), substring(uniqueString(deployment().name), 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" + } + } + } + } + }, + "virtualNetwork": { "existing": true, - "type": "Microsoft.KeyVault/vaults", - "apiVersion": "2022-07-01", - "name": "[parameters('keyVaultName')]" + "type": "Microsoft.Network/virtualNetworks", + "apiVersion": "2024-01-01", + "name": "[parameters('virtualNetworkName')]" }, - "secret": { - "type": "Microsoft.KeyVault/vaults/secrets", - "apiVersion": "2022-07-01", - "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('name'))]", - "tags": "[parameters('tags')]", + "subnet": { + "type": "Microsoft.Network/virtualNetworks/subnets", + "apiVersion": "2024-05-01", + "name": "[format('{0}/{1}', parameters('virtualNetworkName'), parameters('name'))]", "properties": { - "contentType": "[parameters('contentType')]", - "attributes": { - "enabled": "[parameters('attributesEnabled')]", - "exp": "[parameters('attributesExp')]", - "nbf": "[parameters('attributesNbf')]" - }, - "value": "[parameters('value')]" - }, - "dependsOn": [ - "keyVault" - ] - }, - "secret_roleAssignments": { + "copy": [ + { + "name": "serviceEndpoints", + "count": "[length(parameters('serviceEndpoints'))]", + "input": { + "service": "[parameters('serviceEndpoints')[copyIndex('serviceEndpoints')]]" + } + } + ], + "addressPrefix": "[parameters('addressPrefix')]", + "addressPrefixes": "[parameters('addressPrefixes')]", + "ipamPoolPrefixAllocations": "[parameters('ipamPoolPrefixAllocations')]", + "networkSecurityGroup": "[if(not(empty(parameters('networkSecurityGroupResourceId'))), createObject('id', parameters('networkSecurityGroupResourceId')), null())]", + "routeTable": "[if(not(empty(parameters('routeTableResourceId'))), createObject('id', parameters('routeTableResourceId')), null())]", + "natGateway": "[if(not(empty(parameters('natGatewayResourceId'))), createObject('id', parameters('natGatewayResourceId')), null())]", + "delegations": "[if(not(empty(parameters('delegation'))), createArray(createObject('name', parameters('delegation'), 'properties', createObject('serviceName', parameters('delegation')))), createArray())]", + "privateEndpointNetworkPolicies": "[parameters('privateEndpointNetworkPolicies')]", + "privateLinkServiceNetworkPolicies": "[parameters('privateLinkServiceNetworkPolicies')]", + "applicationGatewayIPConfigurations": "[parameters('applicationGatewayIPConfigurations')]", + "serviceEndpointPolicies": "[parameters('serviceEndpointPolicies')]", + "defaultOutboundAccess": "[parameters('defaultOutboundAccess')]", + "sharingScope": "[parameters('sharingScope')]" + } + }, + "subnet_roleAssignments": { "copy": { - "name": "secret_roleAssignments", + "name": "subnet_roleAssignments", "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.KeyVault/vaults/{0}/secrets/{1}', parameters('keyVaultName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.KeyVault/vaults/secrets', parameters('keyVaultName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "scope": "[format('Microsoft.Network/virtualNetworks/{0}/subnets/{1}', parameters('virtualNetworkName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), 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]", @@ -10556,2686 +8710,1528 @@ "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" }, "dependsOn": [ - "secret" + "subnet" ] } }, "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the virtual network peering was deployed into." + }, + "value": "[resourceGroup().name]" + }, "name": { "type": "string", "metadata": { - "description": "The name of the secret." + "description": "The name of the virtual network peering." }, "value": "[parameters('name')]" }, "resourceId": { "type": "string", "metadata": { - "description": "The resource ID of the secret." + "description": "The resource ID of the virtual network peering." }, - "value": "[resourceId('Microsoft.KeyVault/vaults/secrets', parameters('keyVaultName'), parameters('name'))]" + "value": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('name'))]" }, - "secretUri": { + "addressPrefix": { "type": "string", "metadata": { - "description": "The uri of the secret." + "description": "The address prefix for the subnet." }, - "value": "[reference('secret').secretUri]" + "value": "[coalesce(tryGet(reference('subnet'), 'addressPrefix'), '')]" }, - "secretUriWithVersion": { - "type": "string", + "addressPrefixes": { + "type": "array", "metadata": { - "description": "The uri with version of the secret." + "description": "List of address prefixes for the subnet." }, - "value": "[reference('secret').secretUriWithVersion]" + "value": "[coalesce(tryGet(reference('subnet'), 'addressPrefixes'), createArray())]" }, - "resourceGroupName": { - "type": "string", + "ipamPoolPrefixAllocations": { + "type": "array", "metadata": { - "description": "The name of the resource group the secret was created in." + "description": "The IPAM pool prefix allocations for the subnet." }, - "value": "[resourceGroup().name]" + "value": "[coalesce(tryGet(reference('subnet'), 'ipamPoolPrefixAllocations'), createArray())]" } } } }, "dependsOn": [ - "keyVault" + "virtualNetwork" ] }, - "keyVault_keys": { + "virtualNetwork_peering_local": { "copy": { - "name": "keyVault_keys", - "count": "[length(coalesce(parameters('keys'), createArray()))]" + "name": "virtualNetwork_peering_local", + "count": "[length(coalesce(parameters('peerings'), createArray()))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-KeyVault-Key-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "name": "[format('{0}-virtualNetworkPeering-local-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "name": { - "value": "[coalesce(parameters('keys'), createArray())[copyIndex()].name]" - }, - "keyVaultName": { + "localVnetName": { "value": "[parameters('name')]" }, - "attributesEnabled": { - "value": "[tryGet(tryGet(coalesce(parameters('keys'), createArray())[copyIndex()], 'attributes'), 'enabled')]" - }, - "attributesExp": { - "value": "[tryGet(tryGet(coalesce(parameters('keys'), createArray())[copyIndex()], 'attributes'), 'exp')]" - }, - "attributesNbf": { - "value": "[tryGet(tryGet(coalesce(parameters('keys'), createArray())[copyIndex()], 'attributes'), 'nbf')]" + "remoteVirtualNetworkResourceId": { + "value": "[coalesce(parameters('peerings'), createArray())[copyIndex()].remoteVirtualNetworkResourceId]" }, - "curveName": "[if(and(not(equals(tryGet(coalesce(parameters('keys'), createArray())[copyIndex()], 'kty'), 'RSA')), not(equals(tryGet(coalesce(parameters('keys'), createArray())[copyIndex()], 'kty'), 'RSA-HSM'))), createObject('value', coalesce(tryGet(coalesce(parameters('keys'), createArray())[copyIndex()], 'curveName'), 'P-256')), createObject('value', null()))]", - "keyOps": { - "value": "[tryGet(coalesce(parameters('keys'), createArray())[copyIndex()], 'keyOps')]" + "name": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'name')]" }, - "keySize": "[if(or(equals(tryGet(coalesce(parameters('keys'), createArray())[copyIndex()], 'kty'), 'RSA'), equals(tryGet(coalesce(parameters('keys'), createArray())[copyIndex()], 'kty'), 'RSA-HSM')), createObject('value', coalesce(tryGet(coalesce(parameters('keys'), createArray())[copyIndex()], 'keySize'), 4096)), createObject('value', null()))]", - "releasePolicy": { - "value": "[coalesce(tryGet(coalesce(parameters('keys'), createArray())[copyIndex()], 'releasePolicy'), createObject())]" + "allowForwardedTraffic": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'allowForwardedTraffic')]" }, - "kty": { - "value": "[coalesce(tryGet(coalesce(parameters('keys'), createArray())[copyIndex()], 'kty'), 'EC')]" + "allowGatewayTransit": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'allowGatewayTransit')]" }, - "tags": { - "value": "[coalesce(tryGet(coalesce(parameters('keys'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + "allowVirtualNetworkAccess": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'allowVirtualNetworkAccess')]" }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('keys'), createArray())[copyIndex()], 'roleAssignments')]" + "doNotVerifyRemoteGateways": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'doNotVerifyRemoteGateways')]" }, - "rotationPolicy": { - "value": "[tryGet(coalesce(parameters('keys'), createArray())[copyIndex()], 'rotationPolicy')]" + "useRemoteGateways": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'useRemoteGateways')]" } }, "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.31.92.45157", - "templateHash": "16374679780625197074" + "version": "0.35.1.17967", + "templateHash": "11179987886456111827" }, - "name": "Key Vault Keys", - "description": "This module deploys a Key Vault Key.", - "owner": "Azure/module-maintainers" - }, - "definitions": { - "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.2.1" - } - } - } + "name": "Virtual Network Peerings", + "description": "This module deploys a Virtual Network Peering." }, "parameters": { - "keyVaultName": { + "name": { "type": "string", + "defaultValue": "[format('peer-{0}-{1}', parameters('localVnetName'), last(split(parameters('remoteVirtualNetworkResourceId'), '/')))]", "metadata": { - "description": "Conditional. The name of the parent key vault. Required if the template is used in a standalone deployment." + "description": "Optional. The Name of VNET Peering resource. If not provided, default value will be localVnetName-remoteVnetName." } }, - "name": { + "localVnetName": { "type": "string", "metadata": { - "description": "Required. The name of the key." + "description": "Conditional. The name of the parent Virtual Network to add the peering to. Required if the template is used in a standalone deployment." } }, - "tags": { - "type": "object", - "nullable": true, + "remoteVirtualNetworkResourceId": { + "type": "string", "metadata": { - "description": "Optional. Resource tags." + "description": "Required. The Resource ID of the VNet that is this Local VNet is being peered to. Should be in the format of a Resource ID." } }, - "attributesEnabled": { + "allowForwardedTraffic": { "type": "bool", "defaultValue": true, "metadata": { - "description": "Optional. Determines whether the object is enabled." - } - }, - "attributesExp": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. Expiry date in seconds since 1970-01-01T00:00:00Z. For security reasons, it is recommended to set an expiration date whenever possible." - } - }, - "attributesNbf": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. Not before date in seconds since 1970-01-01T00:00:00Z." - } - }, - "curveName": { - "type": "string", - "defaultValue": "P-256", - "allowedValues": [ - "P-256", - "P-256K", - "P-384", - "P-521" - ], - "metadata": { - "description": "Optional. The elliptic curve name." - } - }, - "keyOps": { - "type": "array", - "nullable": true, - "allowedValues": [ - "decrypt", - "encrypt", - "import", - "sign", - "unwrapKey", - "verify", - "wrapKey" - ], - "metadata": { - "description": "Optional. Array of JsonWebKeyOperation." - } - }, - "keySize": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The key size in bits. For example: 2048, 3072, or 4096 for RSA." + "description": "Optional. Whether the forwarded traffic from the VMs in the local virtual network will be allowed/disallowed in remote virtual network. Default is true." } }, - "kty": { - "type": "string", - "defaultValue": "EC", - "allowedValues": [ - "EC", - "EC-HSM", - "RSA", - "RSA-HSM" - ], + "allowGatewayTransit": { + "type": "bool", + "defaultValue": false, "metadata": { - "description": "Optional. The type of the key." + "description": "Optional. If gateway links can be used in remote virtual networking to link to this virtual network. Default is false." } }, - "releasePolicy": { - "type": "object", - "nullable": true, + "allowVirtualNetworkAccess": { + "type": "bool", + "defaultValue": true, "metadata": { - "description": "Optional. Key release policy." + "description": "Optional. Whether the VMs in the local virtual network space would be able to access the VMs in remote virtual network space. Default is true." } }, - "roleAssignments": { - "type": "array", - "items": { - "$ref": "#/definitions/roleAssignmentType" - }, - "nullable": true, + "doNotVerifyRemoteGateways": { + "type": "bool", + "defaultValue": true, "metadata": { - "description": "Optional. Array of role assignments to create." + "description": "Optional. If we need to verify the provisioning state of the remote gateway. Default is true." } }, - "rotationPolicy": { - "type": "object", - "nullable": true, + "useRemoteGateways": { + "type": "bool", + "defaultValue": false, "metadata": { - "description": "Optional. Key rotation policy properties object." - } - } - }, - "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)))))]" + "description": "Optional. If remote gateways can be used on this virtual network. If the flag is set to true, and allowGatewayTransit on remote peering is also true, virtual network will use gateways of remote virtual network for transit. Only one peering can have this flag set to true. This flag cannot be set if virtual network already has a gateway. Default is false." } - ], - "builtInRoleNames": { - "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", - "Key Vault Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00482a5a-887f-4fb3-b363-3b7fe8e74483')]", - "Key Vault Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f25e0fa2-a7c8-4377-a976-54943a77a395')]", - "Key Vault Crypto Officer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '14b46e9e-c2b7-41b4-b07b-48a6ebf60603')]", - "Key Vault Crypto Service Encryption User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e147488a-f6f5-4113-8e2d-b22465e65bf6')]", - "Key Vault Crypto User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '12338af0-0e69-4776-bea7-57ae8d297424')]", - "Key Vault Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21090545-7ca7-4776-b22c-e363652d74d2')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, - "resources": { - "keyVault": { - "existing": true, - "type": "Microsoft.KeyVault/vaults", - "apiVersion": "2022-07-01", - "name": "[parameters('keyVaultName')]" - }, - "key": { - "type": "Microsoft.KeyVault/vaults/keys", - "apiVersion": "2022-07-01", - "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('name'))]", - "tags": "[parameters('tags')]", - "properties": "[shallowMerge(createArray(createObject('attributes', createObject('enabled', parameters('attributesEnabled'), 'exp', parameters('attributesExp'), 'nbf', parameters('attributesNbf')), 'curveName', parameters('curveName'), 'keyOps', parameters('keyOps'), 'keySize', parameters('keySize'), 'kty', parameters('kty'), 'release_policy', coalesce(parameters('releasePolicy'), createObject())), if(not(empty(parameters('rotationPolicy'))), createObject('rotationPolicy', parameters('rotationPolicy')), createObject())))]", - "dependsOn": [ - "keyVault" - ] - }, - "key_roleAssignments": { - "copy": { - "name": "key_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.KeyVault/vaults/{0}/keys/{1}', parameters('keyVaultName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.KeyVault/vaults/keys', parameters('keyVaultName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "resources": [ + { + "type": "Microsoft.Network/virtualNetworks/virtualNetworkPeerings", + "apiVersion": "2024-01-01", + "name": "[format('{0}/{1}', parameters('localVnetName'), parameters('name'))]", "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": [ - "key" - ] + "allowForwardedTraffic": "[parameters('allowForwardedTraffic')]", + "allowGatewayTransit": "[parameters('allowGatewayTransit')]", + "allowVirtualNetworkAccess": "[parameters('allowVirtualNetworkAccess')]", + "doNotVerifyRemoteGateways": "[parameters('doNotVerifyRemoteGateways')]", + "useRemoteGateways": "[parameters('useRemoteGateways')]", + "remoteVirtualNetwork": { + "id": "[parameters('remoteVirtualNetworkResourceId')]" + } + } } - }, + ], "outputs": { - "keyUri": { - "type": "string", - "metadata": { - "description": "The uri of the key." - }, - "value": "[reference('key').keyUri]" - }, - "keyUriWithVersion": { + "resourceGroupName": { "type": "string", "metadata": { - "description": "The uri with version of the key." + "description": "The resource group the virtual network peering was deployed into." }, - "value": "[reference('key').keyUriWithVersion]" + "value": "[resourceGroup().name]" }, "name": { "type": "string", "metadata": { - "description": "The name of the key." + "description": "The name of the virtual network peering." }, "value": "[parameters('name')]" }, "resourceId": { "type": "string", "metadata": { - "description": "The resource ID of the key." - }, - "value": "[resourceId('Microsoft.KeyVault/vaults/keys', parameters('keyVaultName'), parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The name of the resource group the key was created in." + "description": "The resource ID of the virtual network peering." }, - "value": "[resourceGroup().name]" + "value": "[resourceId('Microsoft.Network/virtualNetworks/virtualNetworkPeerings', parameters('localVnetName'), parameters('name'))]" } } } }, "dependsOn": [ - "keyVault" + "virtualNetwork", + "virtualNetwork_subnets" ] }, - "keyVault_privateEndpoints": { + "virtualNetwork_peering_remote": { "copy": { - "name": "keyVault_privateEndpoints", - "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]" + "name": "virtualNetwork_peering_remote", + "count": "[length(coalesce(parameters('peerings'), createArray()))]" }, + "condition": "[coalesce(tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'remotePeeringEnabled'), false())]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-keyVault-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "resourceGroup": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupName'), '')]", + "name": "[format('{0}-virtualNetworkPeering-remote-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "subscriptionId": "[split(coalesce(parameters('peerings'), createArray())[copyIndex()].remoteVirtualNetworkResourceId, '/')[2]]", + "resourceGroup": "[split(coalesce(parameters('peerings'), createArray())[copyIndex()].remoteVirtualNetworkResourceId, '/')[4]]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "name": { - "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'name'), format('pep-{0}-{1}-{2}', last(split(resourceId('Microsoft.KeyVault/vaults', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'vault'), copyIndex()))]" - }, - "privateLinkServiceConnections": "[if(not(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true())), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.KeyVault/vaults', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'vault'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.KeyVault/vaults', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'vault')))))), createObject('value', null()))]", - "manualPrivateLinkServiceConnections": "[if(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true()), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.KeyVault/vaults', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'vault'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.KeyVault/vaults', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'vault')), 'requestMessage', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualConnectionRequestMessage'), 'Manual approval required.'))))), createObject('value', null()))]", - "subnetResourceId": { - "value": "[coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId]" - }, - "enableTelemetry": { - "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'enableTelemetry'), parameters('enableTelemetry'))]" - }, - "location": { - "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'location'), reference(split(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location)]" - }, - "lock": { - "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" + "localVnetName": { + "value": "[last(split(coalesce(parameters('peerings'), createArray())[copyIndex()].remoteVirtualNetworkResourceId, '/'))]" }, - "privateDnsZoneGroup": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroup')]" + "remoteVirtualNetworkResourceId": { + "value": "[resourceId('Microsoft.Network/virtualNetworks', parameters('name'))]" }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'roleAssignments')]" + "name": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'remotePeeringName')]" }, - "tags": { - "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + "allowForwardedTraffic": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'remotePeeringAllowForwardedTraffic')]" }, - "customDnsConfigs": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customDnsConfigs')]" + "allowGatewayTransit": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'remotePeeringAllowGatewayTransit')]" }, - "ipConfigurations": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'ipConfigurations')]" + "allowVirtualNetworkAccess": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'remotePeeringAllowVirtualNetworkAccess')]" }, - "applicationSecurityGroupResourceIds": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'applicationSecurityGroupResourceIds')]" + "doNotVerifyRemoteGateways": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'remotePeeringDoNotVerifyRemoteGateways')]" }, - "customNetworkInterfaceName": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customNetworkInterfaceName')]" + "useRemoteGateways": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'remotePeeringUseRemoteGateways')]" } }, "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.30.23.60470", - "templateHash": "6724714132049298262" + "version": "0.35.1.17967", + "templateHash": "11179987886456111827" }, - "name": "Private Endpoints", - "description": "This module deploys a Private Endpoint.", - "owner": "Azure/module-maintainers" + "name": "Virtual Network Peerings", + "description": "This module deploys a Virtual Network Peering." }, - "definitions": { - "privateDnsZoneGroupType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the Private DNS Zone Group." - } - }, - "privateDnsZoneGroupConfigs": { - "type": "array", - "items": { - "$ref": "#/definitions/privateDnsZoneGroupConfigType" - }, - "metadata": { - "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." - } - } - }, + "parameters": { + "name": { + "type": "string", + "defaultValue": "[format('peer-{0}-{1}', parameters('localVnetName'), last(split(parameters('remoteVirtualNetworkResourceId'), '/')))]", "metadata": { - "__bicep_export!": true + "description": "Optional. The Name of VNET Peering resource. If not provided, default value will be localVnetName-remoteVnetName." } }, - "ipConfigurationType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the resource that is unique within a resource group." - } - }, - "properties": { - "type": "object", - "properties": { - "groupId": { - "type": "string", - "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." - } - }, - "memberName": { - "type": "string", - "metadata": { - "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." - } - }, - "privateIPAddress": { - "type": "string", - "metadata": { - "description": "Required. A private IP address obtained from the private endpoint's subnet." - } - } - }, - "metadata": { - "description": "Required. Properties of private endpoint IP configurations." - } - } - }, + "localVnetName": { + "type": "string", "metadata": { - "__bicep_export!": true + "description": "Conditional. The name of the parent Virtual Network to add the peering to. Required if the template is used in a standalone deployment." } }, - "manualPrivateLinkServiceConnectionType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the private link service connection." - } - }, - "properties": { - "type": "object", - "properties": { - "groupIds": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." - } - }, - "privateLinkServiceId": { - "type": "string", - "metadata": { - "description": "Required. The resource id of private link service." - } - }, - "requestMessage": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." - } - } - }, - "metadata": { - "description": "Required. Properties of private link service connection." - } - } - }, + "remoteVirtualNetworkResourceId": { + "type": "string", "metadata": { - "__bicep_export!": true + "description": "Required. The Resource ID of the VNet that is this Local VNet is being peered to. Should be in the format of a Resource ID." } }, - "privateLinkServiceConnectionType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the private link service connection." - } - }, - "properties": { - "type": "object", - "properties": { - "groupIds": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." - } - }, - "privateLinkServiceId": { - "type": "string", - "metadata": { - "description": "Required. The resource id of private link service." - } - }, - "requestMessage": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." - } - } - }, - "metadata": { - "description": "Required. Properties of private link service connection." - } - } - }, + "allowForwardedTraffic": { + "type": "bool", + "defaultValue": true, "metadata": { - "__bicep_export!": true + "description": "Optional. Whether the forwarded traffic from the VMs in the local virtual network will be allowed/disallowed in remote virtual network. Default is true." } }, - "customDnsConfigType": { - "type": "object", - "properties": { - "fqdn": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. FQDN that resolves to private endpoint IP address." - } - }, - "ipAddresses": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. A list of private IP addresses of the private endpoint." - } - } - }, + "allowGatewayTransit": { + "type": "bool", + "defaultValue": false, "metadata": { - "__bicep_export!": true + "description": "Optional. If gateway links can be used in remote virtual networking to link to this virtual network. Default is false." } }, - "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." - } - } - }, + "allowVirtualNetworkAccess": { + "type": "bool", + "defaultValue": true, "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.2.1" - } + "description": "Optional. Whether the VMs in the local virtual network space would be able to access the VMs in remote virtual network space. Default is true." } }, - "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." - } - } - }, + "doNotVerifyRemoteGateways": { + "type": "bool", + "defaultValue": true, "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "private-dns-zone-group/main.bicep" - } + "description": "Optional. If we need to verify the provisioning state of the remote gateway. Default is true." } }, - "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." - } - } - }, + "useRemoteGateways": { + "type": "bool", + "defaultValue": false, "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.2.1" - } + "description": "Optional. If remote gateways can be used on this virtual network. If the flag is set to true, and allowGatewayTransit on remote peering is also true, virtual network will use gateways of remote virtual network for transit. Only one peering can have this flag set to true. This flag cannot be set if virtual network already has a gateway. Default is false." } } }, - "parameters": { - "name": { - "type": "string", - "metadata": { - "description": "Required. Name of the private endpoint resource to create." + "resources": [ + { + "type": "Microsoft.Network/virtualNetworks/virtualNetworkPeerings", + "apiVersion": "2024-01-01", + "name": "[format('{0}/{1}', parameters('localVnetName'), parameters('name'))]", + "properties": { + "allowForwardedTraffic": "[parameters('allowForwardedTraffic')]", + "allowGatewayTransit": "[parameters('allowGatewayTransit')]", + "allowVirtualNetworkAccess": "[parameters('allowVirtualNetworkAccess')]", + "doNotVerifyRemoteGateways": "[parameters('doNotVerifyRemoteGateways')]", + "useRemoteGateways": "[parameters('useRemoteGateways')]", + "remoteVirtualNetwork": { + "id": "[parameters('remoteVirtualNetworkResourceId')]" + } } - }, - "subnetResourceId": { + } + ], + "outputs": { + "resourceGroupName": { "type": "string", "metadata": { - "description": "Required. Resource ID of the subnet where the endpoint needs to be created." - } - }, - "applicationSecurityGroupResourceIds": { - "type": "array", - "items": { - "type": "string" + "description": "The resource group the virtual network peering was deployed into." }, - "nullable": true, - "metadata": { - "description": "Optional. Application security groups in which the private endpoint IP configuration is included." - } + "value": "[resourceGroup().name]" }, - "customNetworkInterfaceName": { + "name": { "type": "string", - "nullable": true, "metadata": { - "description": "Optional. The custom name of the network interface attached to the private endpoint." - } - }, - "ipConfigurations": { - "type": "array", - "items": { - "$ref": "#/definitions/ipConfigurationType" + "description": "The name of the virtual network peering." }, - "nullable": true, - "metadata": { - "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." - } - }, - "privateDnsZoneGroup": { - "$ref": "#/definitions/privateDnsZoneGroupType", - "nullable": true, - "metadata": { - "description": "Optional. The private DNS zone group to configure for the private endpoint." - } + "value": "[parameters('name')]" }, - "location": { + "resourceId": { "type": "string", - "defaultValue": "[resourceGroup().location]", - "metadata": { - "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" + "description": "The resource ID of the virtual network peering." }, - "nullable": true, - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "tags": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." - } - }, - "customDnsConfigs": { - "type": "array", - "items": { - "$ref": "#/definitions/customDnsConfigType" + "value": "[resourceId('Microsoft.Network/virtualNetworks/virtualNetworkPeerings', parameters('localVnetName'), parameters('name'))]" + } + } + } + }, + "dependsOn": [ + "virtualNetwork", + "virtualNetwork_subnets" + ] + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the virtual network was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the virtual network." + }, + "value": "[resourceId('Microsoft.Network/virtualNetworks', parameters('name'))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the virtual network." + }, + "value": "[parameters('name')]" + }, + "subnetNames": { + "type": "array", + "metadata": { + "description": "The names of the deployed subnets." + }, + "copy": { + "count": "[length(coalesce(parameters('subnets'), createArray()))]", + "input": "[reference(format('virtualNetwork_subnets[{0}]', copyIndex())).outputs.name.value]" + } + }, + "subnetResourceIds": { + "type": "array", + "metadata": { + "description": "The resource IDs of the deployed subnets." + }, + "copy": { + "count": "[length(coalesce(parameters('subnets'), createArray()))]", + "input": "[reference(format('virtualNetwork_subnets[{0}]', copyIndex())).outputs.resourceId.value]" + } + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('virtualNetwork', '2024-05-01', 'full').location]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', take(format('{0}-app-nsg', parameters('resourceToken')), 64))]", + "[resourceId('Microsoft.Resources/deployments', take(format('{0}-bastion-nsg', parameters('resourceToken')), 64))]", + "[resourceId('Microsoft.Resources/deployments', take(format('{0}-default-nsg', parameters('resourceToken')), 64))]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('{0}-bastion', parameters('resourceToken')), 64)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[format('bas-{0}', parameters('resourceToken'))]" + }, + "location": { + "value": "[parameters('location')]" + }, + "skuName": { + "value": "Standard" + }, + "virtualNetworkResourceId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', take(format('{0}-vnet', parameters('resourceToken')), 64)), '2022-09-01').outputs.resourceId.value]" + }, + "diagnosticSettings": { + "value": [ + { + "workspaceResourceId": "[parameters('logAnalyticsWorkspaceId')]" + } + ] + }, + "tags": { + "value": "[parameters('tags')]" + }, + "disableCopyPaste": { + "value": false + }, + "enableFileCopy": { + "value": true + }, + "enableIpConnect": { + "value": true + }, + "enableShareableLink": { + "value": true + }, + "publicIPAddressObject": { + "value": { + "name": "[format('pip-bas-{0}', parameters('resourceToken'))]", + "skuName": "Standard", + "publicIPAllocationMethod": "Static", + "diagnosticSettings": [ + { + "workspaceResourceId": "[parameters('logAnalyticsWorkspaceId')]" + } + ], + "tags": "[parameters('tags')]" + } + } + }, + "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.33.93.31351", + "templateHash": "2586599138991803385" + }, + "name": "Bastion Hosts", + "description": "This module deploys a Bastion Host." + }, + "definitions": { + "diagnosticSettingLogsOnlyType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } }, - "nullable": true, - "metadata": { - "description": "Optional. Custom DNS configurations." - } - }, - "manualPrivateLinkServiceConnections": { - "type": "array", - "items": { - "$ref": "#/definitions/manualPrivateLinkServiceConnectionType" + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } }, - "nullable": true, - "metadata": { - "description": "Optional. 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." + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } } - }, - "privateLinkServiceConnections": { - "type": "array", - "items": { - "$ref": "#/definitions/privateLinkServiceConnectionType" - }, - "nullable": true, - "metadata": { - "description": "Optional. A grouping of information about the connection to the remote resource." - } - }, - "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": "2024-03-01", - "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.9.0', '.', '-'), 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": "2023-11-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": "[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": "2022-09-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.30.23.60470", - "templateHash": "12329174801198479603" - }, - "name": "Private Endpoint Private DNS Zone Groups", - "description": "This module deploys a Private Endpoint Private DNS Zone Group.", - "owner": "Azure/module-maintainers" - }, - "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 - } - } - }, - "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." - } - } - }, - "variables": { - "copy": [ - { - "name": "privateDnsZoneConfigsVar", - "count": "[length(parameters('privateDnsZoneConfigs'))]", - "input": { - "name": "[coalesce(tryGet(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')], 'name'), last(split(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId, '/')))]", - "properties": { - "privateDnsZoneId": "[parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId]" - } - } - } - ] - }, - "resources": { - "privateEndpoint": { - "existing": true, - "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2023-11-01", - "name": "[parameters('privateEndpointName')]" - }, - "privateDnsZoneGroup": { - "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2023-11-01", - "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", - "properties": { - "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" - }, - "dependsOn": [ - "privateEndpoint" - ] - } - }, - "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', '2023-11-01', 'full').location]" - }, - "customDnsConfig": { - "type": "array", - "items": { - "$ref": "#/definitions/customDnsConfigType" - }, - "metadata": { - "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))]" - } + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log 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." } } }, - "dependsOn": [ - "keyVault" - ] - } - }, - "outputs": { - "resourceId": { - "type": "string", "metadata": { - "description": "The resource ID of the key vault." + "description": "An AVM-aligned type for a diagnostic setting. To be used if only logs are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "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." + } + } }, - "value": "[resourceId('Microsoft.KeyVault/vaults', parameters('name'))]" + "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.5.1" + } + } }, - "resourceGroupName": { + "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.5.1" + } + } + } + }, + "parameters": { + "name": { "type": "string", "metadata": { - "description": "The name of the resource group the key vault was created in." - }, - "value": "[resourceGroup().name]" + "description": "Required. Name of the Azure Bastion resource." + } }, - "name": { + "location": { "type": "string", + "defaultValue": "[resourceGroup().location]", "metadata": { - "description": "The name of the key vault." - }, - "value": "[parameters('name')]" + "description": "Optional. Location for all resources." + } }, - "uri": { + "virtualNetworkResourceId": { "type": "string", "metadata": { - "description": "The URI of the key vault." - }, - "value": "[reference('keyVault').vaultUri]" + "description": "Required. Shared services Virtual Network resource Id." + } }, - "location": { + "bastionSubnetPublicIpResourceId": { "type": "string", + "defaultValue": "", "metadata": { - "description": "The location the resource was deployed into." + "description": "Optional. The Public IP resource ID to associate to the azureBastionSubnet. If empty, then the Public IP that is created as part of this module will be applied to the azureBastionSubnet. This parameter is ignored when enablePrivateOnlyBastion is true." + } + }, + "publicIPAddressObject": { + "type": "object", + "defaultValue": { + "name": "[format('{0}-pip', parameters('name'))]" }, - "value": "[reference('keyVault', '2022-07-01', 'full').location]" + "metadata": { + "description": "Optional. Specifies the properties of the Public IP to create and be used by Azure Bastion, if no existing public IP was provided. This parameter is ignored when enablePrivateOnlyBastion is true." + } }, - "privateEndpoints": { + "diagnosticSettings": { "type": "array", "items": { - "$ref": "#/definitions/privateEndpointOutputType" + "$ref": "#/definitions/diagnosticSettingLogsOnlyType" }, + "nullable": true, "metadata": { - "description": "The private endpoints of the key vault." - }, - "copy": { - "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]", - "input": { - "name": "[reference(format('keyVault_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", - "resourceId": "[reference(format('keyVault_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", - "groupId": "[reference(format('keyVault_privateEndpoints[{0}]', copyIndex())).outputs.groupId.value]", - "customDnsConfigs": "[reference(format('keyVault_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfig.value]", - "networkInterfaceResourceIds": "[reference(format('keyVault_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceResourceIds.value]" - } + "description": "Optional. The diagnostic settings of the service." } }, - "secrets": { + "lock": { + "$ref": "#/definitions/lockType", + "nullable": true, + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "skuName": { + "type": "string", + "defaultValue": "Basic", + "allowedValues": [ + "Basic", + "Developer", + "Premium", + "Standard" + ], + "metadata": { + "description": "Optional. The SKU of this Bastion Host." + } + }, + "disableCopyPaste": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Choose to disable or enable Copy Paste. For Basic and Developer SKU Copy/Paste is always enabled." + } + }, + "enableFileCopy": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Choose to disable or enable File Copy. Not supported for Basic and Developer SKU." + } + }, + "enableIpConnect": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Choose to disable or enable IP Connect. Not supported for Basic and Developer SKU." + } + }, + "enableKerberos": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Choose to disable or enable Kerberos authentication. Not supported for Developer SKU." + } + }, + "enableShareableLink": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Choose to disable or enable Shareable Link. Not supported for Basic and Developer SKU." + } + }, + "enableSessionRecording": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Choose to disable or enable Session Recording feature. The Premium SKU is required for this feature. If Session Recording is enabled, the Native client support will be disabled." + } + }, + "enablePrivateOnlyBastion": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Choose to disable or enable Private-only Bastion deployment. The Premium SKU is required for this feature." + } + }, + "scaleUnits": { + "type": "int", + "defaultValue": 2, + "metadata": { + "description": "Optional. The scale units for the Bastion Host resource. The Basic and Developer SKU only support 2 scale units." + } + }, + "roleAssignments": { "type": "array", "items": { - "$ref": "#/definitions/credentialOutputType" + "$ref": "#/definitions/roleAssignmentType" }, + "nullable": true, "metadata": { - "description": "The properties of the created secrets." - }, - "copy": { - "count": "[length(range(0, length(coalesce(parameters('secrets'), createArray()))))]", - "input": { - "resourceId": "[reference(format('keyVault_secrets[{0}]', range(0, length(coalesce(parameters('secrets'), createArray())))[copyIndex()])).outputs.resourceId.value]", - "uri": "[reference(format('keyVault_secrets[{0}]', range(0, length(coalesce(parameters('secrets'), createArray())))[copyIndex()])).outputs.secretUri.value]", - "uriWithVersion": "[reference(format('keyVault_secrets[{0}]', range(0, length(coalesce(parameters('secrets'), createArray())))[copyIndex()])).outputs.secretUriWithVersion.value]" - } + "description": "Optional. Array of role assignments to create." } }, - "keys": { + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "zones": { "type": "array", "items": { - "$ref": "#/definitions/credentialOutputType" + "type": "int" }, + "defaultValue": [], + "allowedValues": [ + 1, + 2, + 3 + ], "metadata": { - "description": "The properties of the created keys." - }, - "copy": { - "count": "[length(range(0, length(coalesce(parameters('keys'), createArray()))))]", - "input": { - "resourceId": "[reference(format('keyVault_keys[{0}]', range(0, length(coalesce(parameters('keys'), createArray())))[copyIndex()])).outputs.resourceId.value]", - "uri": "[reference(format('keyVault_keys[{0}]', range(0, length(coalesce(parameters('keys'), createArray())))[copyIndex()])).outputs.keyUri.value]", - "uriWithVersion": "[reference(format('keyVault_keys[{0}]', range(0, length(coalesce(parameters('keys'), createArray())))[copyIndex()])).outputs.keyUriWithVersion.value]" - } + "description": "Optional. A list of availability zones denoting where the Bastion Host resource needs to come from. This is not supported for the Developer SKU." } } - } - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', 'private-dns-keyvault-deployment')]" - ] - } - ], - "outputs": { - "resourceId": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', take(format('{0}-keyvault-deployment', variables('nameFormatted')), 64)), '2022-09-01').outputs.resourceId.value]" - }, - "name": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', take(format('{0}-keyvault-deployment', variables('nameFormatted')), 64)), '2022-09-01').outputs.name.value]" - } - } - } - }, - "dependsOn": [ - "logAnalyticsWorkspace", - "network" - ] - }, - "containerRegistry": { - "condition": "[parameters('acrEnabled')]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[take(format('{0}-container-registry-deployment', parameters('name')), 64)]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[format('cr{0}{1}', parameters('name'), variables('resourceToken'))]" - }, - "location": { - "value": "[parameters('location')]" - }, - "networkIsolation": { - "value": "[parameters('networkIsolation')]" - }, - "virtualNetworkResourceId": "[if(parameters('networkIsolation'), createObject('value', reference('network').outputs.virtualNetworkId.value), createObject('value', ''))]", - "virtualNetworkSubnetResourceId": "[if(parameters('networkIsolation'), createObject('value', reference('network').outputs.vmSubnetId.value), createObject('value', ''))]", - "logAnalyticsWorkspaceResourceId": { - "value": "[reference('logAnalyticsWorkspace').outputs.resourceId.value]" - }, - "tags": { - "value": "[variables('allTags')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "2158520837294746606" - } - }, - "parameters": { - "name": { - "type": "string", - "minLength": 5, - "metadata": { - "description": "Name of the Container Registry." - } - }, - "location": { - "type": "string", - "metadata": { - "description": "Specifies the location for all the Azure resources." - } - }, - "tags": { - "type": "object", - "defaultValue": {}, - "metadata": { - "description": "Optional. Tags to be applied to the resources." - } - }, - "virtualNetworkResourceId": { - "type": "string", - "metadata": { - "description": "Resource ID of the virtual network to link the private DNS zones." - } - }, - "virtualNetworkSubnetResourceId": { - "type": "string", - "metadata": { - "description": "Resource ID of the subnet for the private endpoint." - } - }, - "logAnalyticsWorkspaceResourceId": { - "type": "string", - "metadata": { - "description": "Resource ID of the Log Analytics workspace to use for diagnostic settings." - } - }, - "networkIsolation": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Specifies whether network isolation is enabled. This will create a private endpoint for the Container Registry and link the private DNS zone." - } - } - }, - "variables": { - "nameFormatted": "[take(toLower(parameters('name')), 50)]" - }, - "resources": [ - { - "condition": "[parameters('networkIsolation')]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "private-dns-acr-deployment", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[format('privatelink.{0}', if(equals(toLower(environment().name), 'azureusgovernment'), 'azurecr.us', 'azurecr.io'))]" }, - "virtualNetworkLinks": { - "value": [ + "variables": { + "copy": [ { - "virtualNetworkResourceId": "[parameters('virtualNetworkResourceId')]" + "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)))))]" } - ] - }, - "tags": { - "value": "[parameters('tags')]" - } - }, - "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.32.4.45862", - "templateHash": "83178825086050429" - }, - "name": "Private DNS Zones", - "description": "This module deploys a Private DNS zone.", - "owner": "Azure/module-maintainers" + ], + "enableReferencedModulesTelemetry": false, + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-bastionhost.{0}.{1}', replace('0.6.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" } } } + } + }, + "azureBastion": { + "type": "Microsoft.Network/bastionHosts", + "apiVersion": "2024-05-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[coalesce(parameters('tags'), createObject())]", + "sku": { + "name": "[parameters('skuName')]" }, - "nullable": true + "zones": "[if(equals(parameters('skuName'), 'Developer'), createArray(), map(parameters('zones'), lambda('zone', string(lambdaVariables('zone')))))]", + "properties": "[union(createObject('scaleUnits', if(or(equals(parameters('skuName'), 'Basic'), equals(parameters('skuName'), 'Developer')), 2, parameters('scaleUnits')), 'ipConfigurations', if(equals(parameters('skuName'), 'Developer'), createArray(), createArray(createObject('name', 'IpConfAzureBastionSubnet', 'properties', union(createObject('subnet', createObject('id', format('{0}/subnets/AzureBastionSubnet', parameters('virtualNetworkResourceId')))), if(not(parameters('enablePrivateOnlyBastion')), createObject('publicIPAddress', createObject('id', if(not(empty(parameters('bastionSubnetPublicIpResourceId'))), parameters('bastionSubnetPublicIpResourceId'), reference('publicIPAddress').outputs.resourceId.value))), createObject())))))), if(equals(parameters('skuName'), 'Developer'), createObject('virtualNetwork', createObject('id', parameters('virtualNetworkResourceId'))), createObject()), if(or(or(equals(parameters('skuName'), 'Basic'), equals(parameters('skuName'), 'Standard')), equals(parameters('skuName'), 'Premium')), createObject('enableKerberos', parameters('enableKerberos')), createObject()), if(or(equals(parameters('skuName'), 'Standard'), equals(parameters('skuName'), 'Premium')), createObject('enableTunneling', if(equals(parameters('skuName'), 'Standard'), true(), if(parameters('enableSessionRecording'), false(), true())), 'disableCopyPaste', parameters('disableCopyPaste'), 'enableFileCopy', parameters('enableFileCopy'), 'enableIpConnect', parameters('enableIpConnect'), 'enableShareableLink', parameters('enableShareableLink')), createObject()), if(equals(parameters('skuName'), 'Premium'), createObject('enableSessionRecording', parameters('enableSessionRecording'), 'enablePrivateOnlyBastion', parameters('enablePrivateOnlyBastion')), createObject()))]", + "dependsOn": [ + "publicIPAddress" + ] }, - "lockType": { - "type": "object", + "azureBastion_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/bastionHosts/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", "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." + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "azureBastion" + ] + }, + "azureBastion_diagnosticSettings": { + "copy": { + "name": "azureBastion_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Network/bastionHosts/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } } - } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" }, - "nullable": true + "dependsOn": [ + "azureBastion" + ] }, - "aType": { - "type": "array", - "items": { - "type": "object", - "properties": { + "azureBastion_roleAssignments": { + "copy": { + "name": "azureBastion_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/bastionHosts/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/bastionHosts', 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": [ + "azureBastion" + ] + }, + "publicIPAddress": { + "condition": "[and(and(empty(parameters('bastionSubnetPublicIpResourceId')), not(equals(parameters('skuName'), 'Developer'))), not(parameters('enablePrivateOnlyBastion')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Bastion-PIP', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } + "value": "[parameters('publicIPAddressObject').name]" }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } + "enableTelemetry": { + "value": "[variables('enableReferencedModulesTelemetry')]" }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } + "location": { + "value": "[parameters('location')]" + }, + "lock": { + "value": "[parameters('lock')]" + }, + "diagnosticSettings": { + "value": "[tryGet(parameters('publicIPAddressObject'), 'diagnosticSettings')]" + }, + "publicIPAddressVersion": { + "value": "[tryGet(parameters('publicIPAddressObject'), 'publicIPAddressVersion')]" + }, + "publicIPAllocationMethod": { + "value": "[tryGet(parameters('publicIPAddressObject'), 'publicIPAllocationMethod')]" + }, + "publicIpPrefixResourceId": { + "value": "[tryGet(parameters('publicIPAddressObject'), 'publicIPPrefixResourceId')]" }, "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } + "value": "[tryGet(parameters('publicIPAddressObject'), 'roleAssignments')]" }, - "aRecords": { - "type": "array", - "items": { + "skuName": { + "value": "[tryGet(parameters('publicIPAddressObject'), 'skuName')]" + }, + "skuTier": { + "value": "[tryGet(parameters('publicIPAddressObject'), 'skuTier')]" + }, + "tags": { + "value": "[coalesce(tryGet(parameters('publicIPAddressObject'), 'tags'), parameters('tags'))]" + }, + "zones": { + "value": "[coalesce(tryGet(parameters('publicIPAddressObject'), 'zones'), if(greater(length(parameters('zones')), 0), parameters('zones'), null()))]" + } + }, + "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.33.93.31351", + "templateHash": "5168739580767459761" + }, + "name": "Public IP Addresses", + "description": "This module deploys a Public IP Address." + }, + "definitions": { + "dnsSettingsType": { "type": "object", "properties": { - "ipv4Address": { + "domainNameLabel": { "type": "string", "metadata": { - "description": "Required. The IPv4 address of this A record." + "description": "Required. The domain name label. The concatenation of the domain name label and the regionalized DNS zone make up the fully qualified domain name associated with the public IP address. If a domain name label is specified, an A DNS record is created for the public IP in the Microsoft Azure DNS system." } - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. The list of A records in the record set." - } - } - } - }, - "nullable": true - }, - "aaaaType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } - }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "aaaaRecords": { - "type": "array", - "items": { - "type": "object", - "properties": { - "ipv6Address": { + }, + "domainNameLabelScope": { "type": "string", + "allowedValues": [ + "NoReuse", + "ResourceGroupReuse", + "SubscriptionReuse", + "TenantReuse" + ], + "nullable": true, "metadata": { - "description": "Required. The IPv6 address of this AAAA record." + "description": "Optional. The domain name label scope. If a domain name label and a domain name label scope are specified, an A DNS record is created for the public IP in the Microsoft Azure DNS system with a hashed value includes in FQDN." + } + }, + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Fully Qualified Domain Name of the A DNS record associated with the public IP. This is the concatenation of the domainNameLabel and the regionalized DNS zone." + } + }, + "reverseFqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The reverse FQDN. A user-visible, fully qualified domain name that resolves to this public IP address. If the reverseFqdn is specified, then a PTR DNS record is created pointing from the IP address in the in-addr.arpa domain to the reverse FQDN." } } + }, + "metadata": { + "__bicep_export!": true } }, - "nullable": true, - "metadata": { - "description": "Optional. The list of AAAA records in the record set." - } - } - } - }, - "nullable": true - }, - "cnameType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } - }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "cnameRecord": { - "type": "object", - "properties": { - "cname": { - "type": "string", - "metadata": { - "description": "Required. The canonical name of the CNAME record." - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. The CNAME record in the record set." - } - } - } - }, - "nullable": true - }, - "mxType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } - }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "mxRecords": { - "type": "array", - "items": { + "ddosSettingsType": { "type": "object", "properties": { - "exchange": { - "type": "string", + "ddosProtectionPlan": { + "type": "object", + "properties": { + "id": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the DDOS protection plan associated with the public IP address." + } + } + }, + "nullable": true, "metadata": { - "description": "Required. The domain name of the mail host for this MX record." + "description": "Optional. The DDoS protection plan associated with the public IP address." } }, - "preference": { - "type": "int", + "protectionMode": { + "type": "string", + "allowedValues": [ + "Enabled" + ], "metadata": { - "description": "Required. The preference value for this MX record." + "description": "Required. The DDoS protection policy customizations." } } + }, + "metadata": { + "__bicep_export!": true } }, - "nullable": true, - "metadata": { - "description": "Optional. The list of MX records in the record set." - } - } - } - }, - "nullable": true - }, - "ptrType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } - }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "ptrRecords": { - "type": "array", - "items": { + "ipTagType": { "type": "object", "properties": { - "ptrdname": { + "ipTagType": { "type": "string", "metadata": { - "description": "Required. The PTR target domain name for this PTR record." + "description": "Required. The IP tag type." + } + }, + "tag": { + "type": "string", + "metadata": { + "description": "Required. The IP tag." } - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. The list of PTR records in the record set." - } - } - } - }, - "nullable": true - }, - "soaType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } - }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "soaRecord": { - "type": "object", - "properties": { - "email": { - "type": "string", - "metadata": { - "description": "Required. The email contact for this SOA record." - } - }, - "expireTime": { - "type": "int", - "metadata": { - "description": "Required. The expire time for this SOA record." - } - }, - "host": { - "type": "string", - "metadata": { - "description": "Required. The domain name of the authoritative name server for this SOA record." - } - }, - "minimumTtl": { - "type": "int", - "metadata": { - "description": "Required. The minimum value for this SOA record. By convention this is used to determine the negative caching duration." - } - }, - "refreshTime": { - "type": "int", - "metadata": { - "description": "Required. The refresh value for this SOA record." - } - }, - "retryTime": { - "type": "int", - "metadata": { - "description": "Required. The retry time for this SOA record." } }, - "serialNumber": { - "type": "int", - "metadata": { - "description": "Required. The serial number for this SOA record." - } + "metadata": { + "__bicep_export!": true } }, - "nullable": true, - "metadata": { - "description": "Optional. The SOA record in the record set." - } - } - } - }, - "nullable": true - }, - "srvType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } - }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "srvRecords": { - "type": "array", - "items": { + "diagnosticSettingFullType": { "type": "object", "properties": { - "priority": { - "type": "int", + "name": { + "type": "string", + "nullable": true, "metadata": { - "description": "Required. The priority value for this SRV record." + "description": "Optional. The name of the diagnostic setting." } }, - "weight": { - "type": "int", + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, "metadata": { - "description": "Required. The weight value for this SRV record." + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." } }, - "port": { - "type": "int", + "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": "Required. The port value for this SRV record." + "description": "Optional. The name of metrics that will be streamed. \"allMetrics\" includes all possible metrics for the resource. Set to `[]` to disable metric collection." } }, - "target": { + "logAnalyticsDestinationType": { "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, "metadata": { - "description": "Required. The target domain name for this SRV record." + "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 both logs & metrics are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.2.1" + } } }, - "nullable": true, - "metadata": { - "description": "Optional. The list of SRV records in the record set." - } - } - } - }, - "nullable": true - }, - "txtType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } - }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "txtRecords": { - "type": "array", - "items": { + "lockType": { "type": "object", "properties": { - "value": { - "type": "array", - "items": { - "type": "string" - }, + "name": { + "type": "string", + "nullable": true, "metadata": { - "description": "Required. The text value of this TXT record." + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of 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.2.1" + } } }, - "nullable": true, - "metadata": { - "description": "Optional. The list of TXT records in the record set." - } - } - } - }, - "nullable": true - }, - "virtualNetworkLinkType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "minLength": 1, - "maxLength": 80, - "metadata": { - "description": "Optional. The resource name." - } - }, - "virtualNetworkResourceId": { - "type": "string", - "metadata": { - "description": "Required. The resource ID of the virtual network to link." - } - }, - "location": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The Azure Region where the resource lives." - } - }, - "registrationEnabled": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Is auto-registration of virtual machine records in the virtual network in the Private DNS zone enabled?." - } - }, - "tags": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Resource tags." - } - }, - "resolutionPolicy": { - "type": "string", - "allowedValues": [ - "Default", - "NxDomainRedirect" - ], - "nullable": true, - "metadata": { - "description": "Optional. The resolution type of the private-dns-zone fallback machanism." - } - } - } - }, - "nullable": true - } - }, - "parameters": { - "name": { - "type": "string", - "metadata": { - "description": "Required. Private DNS zone name." - } - }, - "a": { - "$ref": "#/definitions/aType", - "metadata": { - "description": "Optional. Array of A records." - } - }, - "aaaa": { - "$ref": "#/definitions/aaaaType", - "metadata": { - "description": "Optional. Array of AAAA records." - } - }, - "cname": { - "$ref": "#/definitions/cnameType", - "metadata": { - "description": "Optional. Array of CNAME records." - } - }, - "mx": { - "$ref": "#/definitions/mxType", - "metadata": { - "description": "Optional. Array of MX records." - } - }, - "ptr": { - "$ref": "#/definitions/ptrType", - "metadata": { - "description": "Optional. Array of PTR records." - } - }, - "soa": { - "$ref": "#/definitions/soaType", - "metadata": { - "description": "Optional. Array of SOA records." - } - }, - "srv": { - "$ref": "#/definitions/srvType", - "metadata": { - "description": "Optional. Array of SRV records." - } - }, - "txt": { - "$ref": "#/definitions/txtType", - "metadata": { - "description": "Optional. Array of TXT records." - } - }, - "virtualNetworkLinks": { - "$ref": "#/definitions/virtualNetworkLinkType", - "metadata": { - "description": "Optional. Array of custom objects describing vNet links of the DNS zone. Each object should contain properties 'virtualNetworkResourceId' and 'registrationEnabled'. The 'vnetResourceId' is a resource ID of a vNet to link, 'registrationEnabled' (bool) enables automatic DNS registration in the zone for the linked vNet." - } - }, - "location": { - "type": "string", - "defaultValue": "global", - "metadata": { - "description": "Optional. The location of the PrivateDNSZone. Should be global." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "tags": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Tags of the resource." - } - }, - "lock": { - "$ref": "#/definitions/lockType", - "metadata": { - "description": "Optional. The lock settings of the service." - } - }, - "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')]", - "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": "2024-03-01", - "name": "[format('46d3xbcp.res.network-privatednszone.{0}.{1}', replace('0.7.0', '.', '-'), 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" - } - } - } - } - }, - "privateDnsZone": { - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('name')]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]" - }, - "privateDnsZone_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/privateDnsZones/{0}', parameters('name'))]", - "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", - "properties": { - "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", - "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" - }, - "dependsOn": [ - "privateDnsZone" - ] - }, - "privateDnsZone_roleAssignments": { - "copy": { - "name": "privateDnsZone_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}', parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones', 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": [ - "privateDnsZone" - ] - }, - "privateDnsZone_A": { - "copy": { - "name": "privateDnsZone_A", - "count": "[length(coalesce(parameters('a'), createArray()))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-ARecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "privateDnsZoneName": { - "value": "[parameters('name')]" - }, - "name": { - "value": "[coalesce(parameters('a'), createArray())[copyIndex()].name]" - }, - "aRecords": { - "value": "[tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'aRecords')]" - }, - "metadata": { - "value": "[tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'metadata')]" - }, - "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'ttl'), 3600)]" - }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'roleAssignments')]" - } - }, - "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.32.4.45862", - "templateHash": "2531120132215940282" - }, - "name": "Private DNS Zone A record", - "description": "This module deploys a Private DNS Zone A record.", - "owner": "Azure/module-maintainers" - }, - "definitions": { "roleAssignmentType": { - "type": "array", - "items": { - "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." - } + "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." } } }, - "nullable": true + "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.2.1" + } + } } }, "parameters": { - "privateDnsZoneName": { + "name": { "type": "string", "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + "description": "Required. The name of the Public IP Address." } }, - "name": { + "publicIpPrefixResourceId": { "type": "string", + "nullable": true, "metadata": { - "description": "Required. The name of the A record." + "description": "Optional. Resource ID of the Public IP Prefix object. This is only needed if you want your Public IPs created in a PIP Prefix." } }, - "aRecords": { + "publicIPAllocationMethod": { + "type": "string", + "defaultValue": "Static", + "allowedValues": [ + "Dynamic", + "Static" + ], + "metadata": { + "description": "Optional. The public IP address allocation method." + } + }, + "zones": { "type": "array", + "items": { + "type": "int" + }, + "defaultValue": [ + 1, + 2, + 3 + ], + "allowedValues": [ + 1, + 2, + 3 + ], + "metadata": { + "description": "Optional. A list of availability zones denoting the IP allocated for the resource needs to come from." + } + }, + "publicIPAddressVersion": { + "type": "string", + "defaultValue": "IPv4", + "allowedValues": [ + "IPv4", + "IPv6" + ], + "metadata": { + "description": "Optional. IP address version." + } + }, + "dnsSettings": { + "$ref": "#/definitions/dnsSettingsType", "nullable": true, "metadata": { - "description": "Optional. The list of A records in the record set." + "description": "Optional. The DNS settings of the public IP address." } }, - "metadata": { - "type": "object", + "ipTags": { + "type": "array", + "items": { + "$ref": "#/definitions/ipTagType" + }, "nullable": true, "metadata": { - "description": "Optional. The metadata attached to the record set." + "description": "Optional. The list of tags associated with the public IP address." } }, - "ttl": { - "type": "int", - "defaultValue": 3600, + "lock": { + "$ref": "#/definitions/lockType", + "nullable": true, "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." + "description": "Optional. The lock settings of the service." } }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", + "skuName": { + "type": "string", + "defaultValue": "Standard", + "allowedValues": [ + "Basic", + "Standard" + ], "metadata": { - "description": "Optional. Array of role assignments to create." - } - } - }, - "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')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", - "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" - } - }, - "resources": { - "privateDnsZone": { - "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" - }, - "A": { - "type": "Microsoft.Network/privateDnsZones/A", - "apiVersion": "2020-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "properties": { - "aRecords": "[parameters('aRecords')]", - "metadata": "[parameters('metadata')]", - "ttl": "[parameters('ttl')]" + "description": "Optional. Name of a public IP address SKU." } }, - "A_roleAssignments": { - "copy": { - "name": "A_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/A/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/A', parameters('privateDnsZoneName'), 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": [ - "A" - ] - } - }, - "outputs": { - "name": { + "skuTier": { "type": "string", + "defaultValue": "Regional", + "allowedValues": [ + "Global", + "Regional" + ], "metadata": { - "description": "The name of the deployed A record." - }, - "value": "[parameters('name')]" + "description": "Optional. Tier of a public IP address SKU." + } }, - "resourceId": { - "type": "string", + "ddosSettings": { + "$ref": "#/definitions/ddosSettingsType", + "nullable": true, "metadata": { - "description": "The resource ID of the deployed A record." - }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/A', parameters('privateDnsZoneName'), parameters('name'))]" + "description": "Optional. The DDoS protection plan configuration associated with the public IP address." + } }, - "resourceGroupName": { + "location": { "type": "string", + "defaultValue": "[resourceGroup().location]", "metadata": { - "description": "The resource group of the deployed A record." - }, - "value": "[resourceGroup().name]" - } - } - } - }, - "dependsOn": [ - "privateDnsZone" - ] - }, - "privateDnsZone_AAAA": { - "copy": { - "name": "privateDnsZone_AAAA", - "count": "[length(coalesce(parameters('aaaa'), createArray()))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-AAAARecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "privateDnsZoneName": { - "value": "[parameters('name')]" - }, - "name": { - "value": "[coalesce(parameters('aaaa'), createArray())[copyIndex()].name]" - }, - "aaaaRecords": { - "value": "[tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'aaaaRecords')]" - }, - "metadata": { - "value": "[tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'metadata')]" - }, - "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'ttl'), 3600)]" - }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'roleAssignments')]" - } - }, - "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.32.4.45862", - "templateHash": "16709340450244912125" + "description": "Optional. Location for all resources." + } }, - "name": "Private DNS Zone AAAA record", - "description": "This module deploys a Private DNS Zone AAAA record.", - "owner": "Azure/module-maintainers" - }, - "definitions": { - "roleAssignmentType": { + "roleAssignments": { "type": "array", "items": { - "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." - } - } - } + "$ref": "#/definitions/roleAssignmentType" }, - "nullable": true - } - }, - "parameters": { - "privateDnsZoneName": { - "type": "string", + "nullable": true, "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + "description": "Optional. Array of role assignments to create." } }, - "name": { - "type": "string", + "enableTelemetry": { + "type": "bool", + "defaultValue": true, "metadata": { - "description": "Required. The name of the AAAA record." + "description": "Optional. Enable/Disable usage telemetry for module." } }, - "aaaaRecords": { - "type": "array", - "nullable": true, + "idleTimeoutInMinutes": { + "type": "int", + "defaultValue": 4, "metadata": { - "description": "Optional. The list of AAAA records in the record set." + "description": "Optional. The idle timeout of the public IP address." } }, - "metadata": { + "tags": { "type": "object", "nullable": true, "metadata": { - "description": "Optional. The metadata attached to the record set." - } - }, - "ttl": { - "type": "int", - "defaultValue": 3600, - "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." + "description": "Optional. Tags of the resource." } }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", + "diagnosticSettings": { + "type": "array", + "items": { + "$ref": "#/definitions/diagnosticSettingFullType" + }, + "nullable": true, "metadata": { - "description": "Optional. Array of role assignments to create." + "description": "Optional. The diagnostic settings of the service." } } }, @@ -13249,40 +10245,82 @@ ], "builtInRoleNames": { "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "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')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" } }, "resources": { - "privateDnsZone": { - "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-publicipaddress.{0}.{1}', replace('0.8.0', '.', '-'), 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" + } + } + } + } }, - "AAAA": { - "type": "Microsoft.Network/privateDnsZones/AAAA", - "apiVersion": "2020-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "publicIpAddress": { + "type": "Microsoft.Network/publicIPAddresses", + "apiVersion": "2024-05-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "sku": { + "name": "[parameters('skuName')]", + "tier": "[parameters('skuTier')]" + }, + "zones": "[map(parameters('zones'), lambda('zone', string(lambdaVariables('zone'))))]", "properties": { - "aaaaRecords": "[parameters('aaaaRecords')]", - "metadata": "[parameters('metadata')]", - "ttl": "[parameters('ttl')]" + "ddosSettings": "[parameters('ddosSettings')]", + "dnsSettings": "[parameters('dnsSettings')]", + "publicIPAddressVersion": "[parameters('publicIPAddressVersion')]", + "publicIPAllocationMethod": "[parameters('publicIPAllocationMethod')]", + "publicIPPrefix": "[if(not(empty(parameters('publicIpPrefixResourceId'))), createObject('id', parameters('publicIpPrefixResourceId')), null())]", + "idleTimeoutInMinutes": "[parameters('idleTimeoutInMinutes')]", + "ipTags": "[parameters('ipTags')]" } }, - "AAAA_roleAssignments": { + "publicIpAddress_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/publicIPAddresses/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "publicIpAddress" + ] + }, + "publicIpAddress_roleAssignments": { "copy": { - "name": "AAAA_roleAssignments", + "name": "publicIpAddress_roleAssignments", "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/AAAA/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/AAAA', parameters('privateDnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "scope": "[format('Microsoft.Network/publicIPAddresses/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/publicIPAddresses', 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]", @@ -13293,1047 +10331,1382 @@ "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" }, "dependsOn": [ - "AAAA" + "publicIpAddress" + ] + }, + "publicIpAddress_diagnosticSettings": { + "copy": { + "name": "publicIpAddress_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Network/publicIPAddresses/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null + } + }, + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "publicIpAddress" ] } }, "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the public IP address was deployed into." + }, + "value": "[resourceGroup().name]" + }, "name": { "type": "string", "metadata": { - "description": "The name of the deployed AAAA record." + "description": "The name of the public IP address." }, "value": "[parameters('name')]" }, "resourceId": { "type": "string", "metadata": { - "description": "The resource ID of the deployed AAAA record." + "description": "The resource ID of the public IP address." }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/AAAA', parameters('privateDnsZoneName'), parameters('name'))]" + "value": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('name'))]" }, - "resourceGroupName": { + "ipAddress": { "type": "string", "metadata": { - "description": "The resource group of the deployed AAAA record." + "description": "The public IP address of the public IP address resource." }, - "value": "[resourceGroup().name]" + "value": "[coalesce(tryGet(reference('publicIpAddress'), 'ipAddress'), '')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('publicIpAddress', '2024-05-01', 'full').location]" } } } + } + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the Azure Bastion was deployed into." }, - "dependsOn": [ - "privateDnsZone" - ] + "value": "[resourceGroup().name]" }, - "privateDnsZone_CNAME": { - "copy": { - "name": "privateDnsZone_CNAME", - "count": "[length(coalesce(parameters('cname'), createArray()))]" + "name": { + "type": "string", + "metadata": { + "description": "The name the Azure Bastion." }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-CNAMERecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "privateDnsZoneName": { - "value": "[parameters('name')]" - }, - "name": { - "value": "[coalesce(parameters('cname'), createArray())[copyIndex()].name]" - }, - "cnameRecord": { - "value": "[tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'cnameRecord')]" - }, - "metadata": { - "value": "[tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'metadata')]" - }, - "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'ttl'), 3600)]" - }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'roleAssignments')]" - } - }, - "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.32.4.45862", - "templateHash": "9976020649752073181" - }, - "name": "Private DNS Zone CNAME record", - "description": "This module deploys a Private DNS Zone CNAME record.", - "owner": "Azure/module-maintainers" - }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } - } - } - }, - "nullable": true - } - }, - "parameters": { - "privateDnsZoneName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the CNAME record." - } - }, - "cnameRecord": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. A CNAME record." - } - }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID the Azure Bastion." + }, + "value": "[resourceId('Microsoft.Network/bastionHosts', parameters('name'))]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('azureBastion', '2024-05-01', 'full').location]" + }, + "ipConfAzureBastionSubnet": { + "type": "object", + "metadata": { + "description": "The Public IPconfiguration object for the AzureBastionSubnet." + }, + "value": "[if(equals(parameters('skuName'), 'Developer'), createObject(), reference('azureBastion').ipConfigurations[0])]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', take(format('{0}-vnet', parameters('resourceToken')), 64))]" + ] + } + ], + "outputs": { + "resourceId": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', take(format('{0}-vnet', parameters('resourceToken')), 64)), '2022-09-01').outputs.resourceId.value]" + }, + "name": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', take(format('{0}-vnet', parameters('resourceToken')), 64)), '2022-09-01').outputs.name.value]" + }, + "bastionName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', take(format('{0}-bastion', parameters('resourceToken')), 64)), '2022-09-01').outputs.name.value]" + }, + "defaultSubnetName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', take(format('{0}-vnet', parameters('resourceToken')), 64)), '2022-09-01').outputs.subnetNames.value[0]]" + }, + "defaultSubnetResourceId": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', take(format('{0}-vnet', parameters('resourceToken')), 64)), '2022-09-01').outputs.subnetResourceIds.value[0]]" + }, + "bastionSubnetName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', take(format('{0}-vnet', parameters('resourceToken')), 64)), '2022-09-01').outputs.subnetNames.value[1]]" + }, + "bastionSubnetResourceId": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', take(format('{0}-vnet', parameters('resourceToken')), 64)), '2022-09-01').outputs.subnetResourceIds.value[1]]" + }, + "appSubnetName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', take(format('{0}-vnet', parameters('resourceToken')), 64)), '2022-09-01').outputs.subnetNames.value[2]]" + }, + "appSubnetResourceId": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', take(format('{0}-vnet', parameters('resourceToken')), 64)), '2022-09-01').outputs.subnetResourceIds.value[2]]" + } + } + } + }, + "dependsOn": [ + "logAnalyticsWorkspace" + ] + }, + "keyvault": { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('{0}-keyvault-deployment', parameters('name')), 64)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[format('kv{0}{1}', parameters('name'), variables('resourceToken'))]" + }, + "location": { + "value": "[parameters('location')]" + }, + "networkIsolation": { + "value": "[parameters('networkIsolation')]" + }, + "virtualNetworkResourceId": "[if(parameters('networkIsolation'), createObject('value', reference('network').outputs.resourceId.value), createObject('value', ''))]", + "virtualNetworkSubnetResourceId": "[if(parameters('networkIsolation'), createObject('value', reference('network').outputs.defaultSubnetResourceId.value), createObject('value', ''))]", + "logAnalyticsWorkspaceResourceId": { + "value": "[reference('logAnalyticsWorkspace').outputs.resourceId.value]" + }, + "roleAssignments": { + "value": "[concat(if(empty(parameters('userObjectId')), createArray(), createArray(createObject('principalId', parameters('userObjectId'), 'principalType', 'User', 'roleDefinitionIdOrName', 'Key Vault Secrets User'))), if(variables('deploySampleApp'), createArray(createObject('principalId', reference('appIdentity').outputs.principalId.value, 'principalType', 'ServicePrincipal', 'roleDefinitionIdOrName', 'Key Vault Secrets User')), createArray()))]" + }, + "secrets": "[if(variables('deploySampleApp'), createObject('value', createArray(createObject('name', variables('authClientSecretName'), 'value', coalesce(parameters('authClientSecret'), '')))), createObject('value', createArray()))]", + "tags": { + "value": "[variables('allTags')]" + } + }, + "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.1.42791", + "templateHash": "8370014477286310434" + } + }, + "definitions": { + "_1.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": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/key-vault/vault:0.12.1" + }, + "description": "An AVM-aligned type for a role assignment." + } + }, + "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.5.1" + } + } + }, + "secretType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the secret." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Resource tags." + } + }, + "attributes": { + "type": "object", + "properties": { + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Defines whether the secret is enabled or disabled." + } + }, + "exp": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Defines when the secret will become invalid. Defined in seconds since 1970-01-01T00:00:00Z." + } + }, + "nbf": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. If set, defines the date from which onwards the secret becomes valid. Defined in seconds since 1970-01-01T00:00:00Z." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. Contains attributes of the secret." + } + }, + "contentType": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The content type of the secret." + } + }, + "value": { + "type": "securestring", + "metadata": { + "description": "Required. The value of the secret. NOTE: \"value\" will never be returned from the service, as APIs using this model are is intended for internal use in ARM deployments. Users should use the data-plane REST service for interaction with vault secrets." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/_1.roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "metadata": { + "description": "The type for a secret output.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/key-vault/vault:0.12.1" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Name of the Key Vault." + } + }, + "location": { + "type": "string", + "metadata": { + "description": "Specifies the location for all the Azure resources." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Tags to be applied to the resources." + } + }, + "virtualNetworkResourceId": { + "type": "string", + "metadata": { + "description": "Resource ID of the virtual network to link the private DNS zones." + } + }, + "virtualNetworkSubnetResourceId": { + "type": "string", + "metadata": { + "description": "Resource ID of the subnet for the private endpoint." + } + }, + "logAnalyticsWorkspaceResourceId": { + "type": "string", + "metadata": { + "description": "Resource ID of the Log Analytics workspace to use for diagnostic settings." + } + }, + "networkIsolation": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Specifies whether network isolation is enabled. This will create a private endpoint for the Key Vault and link the private DNS zone." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "secrets": { + "type": "array", + "items": { + "$ref": "#/definitions/secretType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of secrets to create in the Key Vault." + } + } + }, + "variables": { + "nameFormatted": "[take(toLower(parameters('name')), 24)]" + }, + "resources": { + "privateDnsZone": { + "condition": "[parameters('networkIsolation')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "private-dns-keyvault-deployment", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[format('privatelink.{0}', if(equals(toLower(environment().name), 'azureusgovernment'), 'vaultcore.usgovcloudapi.net', 'vaultcore.azure.net'))]" + }, + "virtualNetworkLinks": { + "value": [ + { + "virtualNetworkResourceId": "[parameters('virtualNetworkResourceId')]" + } + ] + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "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.32.4.45862", + "templateHash": "83178825086050429" + }, + "name": "Private DNS Zones", + "description": "This module deploys a Private DNS zone.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata attached to the record set." - } - }, - "ttl": { - "type": "int", - "defaultValue": 3600, - "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." } }, - "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')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", - "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + "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'." } }, - "resources": { - "privateDnsZone": { - "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" - }, - "CNAME": { - "type": "Microsoft.Network/privateDnsZones/CNAME", - "apiVersion": "2020-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "properties": { - "cnameRecord": "[parameters('cnameRecord')]", - "metadata": "[parameters('metadata')]", - "ttl": "[parameters('ttl')]" - } - }, - "CNAME_roleAssignments": { - "copy": { - "name": "CNAME_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/CNAME/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/CNAME', parameters('privateDnsZoneName'), 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": [ - "CNAME" - ] + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." } }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the deployed CNAME record." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the deployed CNAME record." - }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/CNAME', parameters('privateDnsZoneName'), parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group of the deployed CNAME record." - }, - "value": "[resourceGroup().name]" + "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." } } } }, - "dependsOn": [ - "privateDnsZone" - ] + "nullable": true }, - "privateDnsZone_MX": { - "copy": { - "name": "privateDnsZone_MX", - "count": "[length(coalesce(parameters('mx'), createArray()))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-MXRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "lockType": { + "type": "object", "properties": { - "expressionEvaluationOptions": { - "scope": "inner" + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } }, - "mode": "Incremental", - "parameters": { - "privateDnsZoneName": { - "value": "[parameters('name')]" - }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "nullable": true + }, + "aType": { + "type": "array", + "items": { + "type": "object", + "properties": { "name": { - "value": "[coalesce(parameters('mx'), createArray())[copyIndex()].name]" + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } }, "metadata": { - "value": "[tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'metadata')]" - }, - "mxRecords": { - "value": "[tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'mxRecords')]" + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } }, "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'ttl'), 3600)]" + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } }, "roleAssignments": { - "value": "[tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'roleAssignments')]" - } - }, - "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.32.4.45862", - "templateHash": "2520323624213076361" - }, - "name": "Private DNS Zone MX record", - "description": "This module deploys a Private DNS Zone MX record.", - "owner": "Azure/module-maintainers" + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } + "aRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "ipv4Address": { + "type": "string", + "metadata": { + "description": "Required. The IPv4 address of this A record." } } - }, - "nullable": true - } - }, - "parameters": { - "privateDnsZoneName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the MX record." } }, + "nullable": true, "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata attached to the record set." - } - }, - "mxRecords": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. The list of MX records in the record set." - } - }, - "ttl": { - "type": "int", - "defaultValue": 3600, - "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } + "description": "Optional. The list of A records in the record set." + } + } + } + }, + "nullable": true + }, + "aaaaType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." } }, - "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')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", - "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." } }, - "resources": { - "privateDnsZone": { - "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" - }, - "MX": { - "type": "Microsoft.Network/privateDnsZones/MX", - "apiVersion": "2020-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "aaaaRecords": { + "type": "array", + "items": { + "type": "object", "properties": { - "metadata": "[parameters('metadata')]", - "mxRecords": "[parameters('mxRecords')]", - "ttl": "[parameters('ttl')]" + "ipv6Address": { + "type": "string", + "metadata": { + "description": "Required. The IPv6 address of this AAAA record." + } + } } }, - "MX_roleAssignments": { - "copy": { - "name": "MX_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/MX/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/MX', parameters('privateDnsZoneName'), 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": [ - "MX" - ] + "nullable": true, + "metadata": { + "description": "Optional. The list of AAAA records in the record set." + } + } + } + }, + "nullable": true + }, + "cnameType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." } }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the deployed MX record." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the deployed MX record." - }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/MX', parameters('privateDnsZoneName'), parameters('name'))]" + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "cnameRecord": { + "type": "object", + "properties": { + "cname": { + "type": "string", + "metadata": { + "description": "Required. The canonical name of the CNAME record." + } + } }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group of the deployed MX record." - }, - "value": "[resourceGroup().name]" + "nullable": true, + "metadata": { + "description": "Optional. The CNAME record in the record set." } } } }, - "dependsOn": [ - "privateDnsZone" - ] + "nullable": true }, - "privateDnsZone_PTR": { - "copy": { - "name": "privateDnsZone_PTR", - "count": "[length(coalesce(parameters('ptr'), createArray()))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-PTRRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "privateDnsZoneName": { - "value": "[parameters('name')]" - }, + "mxType": { + "type": "array", + "items": { + "type": "object", + "properties": { "name": { - "value": "[coalesce(parameters('ptr'), createArray())[copyIndex()].name]" + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } }, "metadata": { - "value": "[tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'metadata')]" - }, - "ptrRecords": { - "value": "[tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'ptrRecords')]" + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } }, "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'ttl'), 3600)]" + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } }, "roleAssignments": { - "value": "[tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'roleAssignments')]" - } - }, - "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.32.4.45862", - "templateHash": "3080404733048745471" - }, - "name": "Private DNS Zone PTR record", - "description": "This module deploys a Private DNS Zone PTR record.", - "owner": "Azure/module-maintainers" + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } + "mxRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "exchange": { + "type": "string", + "metadata": { + "description": "Required. The domain name of the mail host for this MX record." + } + }, + "preference": { + "type": "int", + "metadata": { + "description": "Required. The preference value for this MX record." } } - }, - "nullable": true - } - }, - "parameters": { - "privateDnsZoneName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the PTR record." } }, + "nullable": true, "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata attached to the record set." - } - }, - "ptrRecords": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. The list of PTR records in the record set." - } - }, - "ttl": { - "type": "int", - "defaultValue": 3600, - "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } + "description": "Optional. The list of MX records in the record set." + } + } + } + }, + "nullable": true + }, + "ptrType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." } }, - "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')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", - "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." } }, - "resources": { - "privateDnsZone": { - "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" - }, - "PTR": { - "type": "Microsoft.Network/privateDnsZones/PTR", - "apiVersion": "2020-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "ptrRecords": { + "type": "array", + "items": { + "type": "object", "properties": { - "metadata": "[parameters('metadata')]", - "ptrRecords": "[parameters('ptrRecords')]", - "ttl": "[parameters('ttl')]" + "ptrdname": { + "type": "string", + "metadata": { + "description": "Required. The PTR target domain name for this PTR record." + } + } } }, - "PTR_roleAssignments": { - "copy": { - "name": "PTR_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/PTR/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/PTR', parameters('privateDnsZoneName'), 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": [ - "PTR" - ] + "nullable": true, + "metadata": { + "description": "Optional. The list of PTR records in the record set." + } + } + } + }, + "nullable": true + }, + "soaType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." } }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the deployed PTR record." + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "soaRecord": { + "type": "object", + "properties": { + "email": { + "type": "string", + "metadata": { + "description": "Required. The email contact for this SOA record." + } }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the deployed PTR record." + "expireTime": { + "type": "int", + "metadata": { + "description": "Required. The expire time for this SOA record." + } }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/PTR', parameters('privateDnsZoneName'), parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group of the deployed PTR record." + "host": { + "type": "string", + "metadata": { + "description": "Required. The domain name of the authoritative name server for this SOA record." + } }, - "value": "[resourceGroup().name]" + "minimumTtl": { + "type": "int", + "metadata": { + "description": "Required. The minimum value for this SOA record. By convention this is used to determine the negative caching duration." + } + }, + "refreshTime": { + "type": "int", + "metadata": { + "description": "Required. The refresh value for this SOA record." + } + }, + "retryTime": { + "type": "int", + "metadata": { + "description": "Required. The retry time for this SOA record." + } + }, + "serialNumber": { + "type": "int", + "metadata": { + "description": "Required. The serial number for this SOA record." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The SOA record in the record set." } } } }, - "dependsOn": [ - "privateDnsZone" - ] + "nullable": true }, - "privateDnsZone_SOA": { - "copy": { - "name": "privateDnsZone_SOA", - "count": "[length(coalesce(parameters('soa'), createArray()))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-SOARecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "privateDnsZoneName": { - "value": "[parameters('name')]" - }, + "srvType": { + "type": "array", + "items": { + "type": "object", + "properties": { "name": { - "value": "[coalesce(parameters('soa'), createArray())[copyIndex()].name]" + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } }, "metadata": { - "value": "[tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'metadata')]" - }, - "soaRecord": { - "value": "[tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'soaRecord')]" + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } }, "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'ttl'), 3600)]" + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } }, "roleAssignments": { - "value": "[tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'roleAssignments')]" - } - }, - "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.32.4.45862", - "templateHash": "6653951445614700931" - }, - "name": "Private DNS Zone SOA record", - "description": "This module deploys a Private DNS Zone SOA record.", - "owner": "Azure/module-maintainers" + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } + "srvRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "priority": { + "type": "int", + "metadata": { + "description": "Required. The priority value for this SRV record." + } + }, + "weight": { + "type": "int", + "metadata": { + "description": "Required. The weight value for this SRV record." + } + }, + "port": { + "type": "int", + "metadata": { + "description": "Required. The port value for this SRV record." + } + }, + "target": { + "type": "string", + "metadata": { + "description": "Required. The target domain name for this SRV record." } } - }, - "nullable": true - } - }, - "parameters": { - "privateDnsZoneName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the SOA record." } }, + "nullable": true, "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata attached to the record set." - } - }, - "soaRecord": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. A SOA record." - } - }, - "ttl": { - "type": "int", - "defaultValue": 3600, - "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } + "description": "Optional. The list of SRV records in the record set." } - }, - "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')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", - "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + } + }, + "nullable": true + }, + "txtType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." } }, - "resources": { - "privateDnsZone": { - "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" - }, - "SOA": { - "type": "Microsoft.Network/privateDnsZones/SOA", - "apiVersion": "2020-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "txtRecords": { + "type": "array", + "items": { + "type": "object", "properties": { - "metadata": "[parameters('metadata')]", - "soaRecord": "[parameters('soaRecord')]", - "ttl": "[parameters('ttl')]" + "value": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The text value of this TXT record." + } + } } }, - "SOA_roleAssignments": { - "copy": { - "name": "SOA_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/SOA/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/SOA', parameters('privateDnsZoneName'), 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": [ - "SOA" - ] + "nullable": true, + "metadata": { + "description": "Optional. The list of TXT records in the record set." + } + } + } + }, + "nullable": true + }, + "virtualNetworkLinkType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "minLength": 1, + "maxLength": 80, + "metadata": { + "description": "Optional. The resource name." + } + }, + "virtualNetworkResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the virtual network to link." + } + }, + "location": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Azure Region where the resource lives." + } + }, + "registrationEnabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Is auto-registration of virtual machine records in the virtual network in the Private DNS zone enabled?." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Resource tags." } }, + "resolutionPolicy": { + "type": "string", + "allowedValues": [ + "Default", + "NxDomainRedirect" + ], + "nullable": true, + "metadata": { + "description": "Optional. The resolution type of the private-dns-zone fallback machanism." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Private DNS zone name." + } + }, + "a": { + "$ref": "#/definitions/aType", + "metadata": { + "description": "Optional. Array of A records." + } + }, + "aaaa": { + "$ref": "#/definitions/aaaaType", + "metadata": { + "description": "Optional. Array of AAAA records." + } + }, + "cname": { + "$ref": "#/definitions/cnameType", + "metadata": { + "description": "Optional. Array of CNAME records." + } + }, + "mx": { + "$ref": "#/definitions/mxType", + "metadata": { + "description": "Optional. Array of MX records." + } + }, + "ptr": { + "$ref": "#/definitions/ptrType", + "metadata": { + "description": "Optional. Array of PTR records." + } + }, + "soa": { + "$ref": "#/definitions/soaType", + "metadata": { + "description": "Optional. Array of SOA records." + } + }, + "srv": { + "$ref": "#/definitions/srvType", + "metadata": { + "description": "Optional. Array of SRV records." + } + }, + "txt": { + "$ref": "#/definitions/txtType", + "metadata": { + "description": "Optional. Array of TXT records." + } + }, + "virtualNetworkLinks": { + "$ref": "#/definitions/virtualNetworkLinkType", + "metadata": { + "description": "Optional. Array of custom objects describing vNet links of the DNS zone. Each object should contain properties 'virtualNetworkResourceId' and 'registrationEnabled'. The 'vnetResourceId' is a resource ID of a vNet to link, 'registrationEnabled' (bool) enables automatic DNS registration in the zone for the linked vNet." + } + }, + "location": { + "type": "string", + "defaultValue": "global", + "metadata": { + "description": "Optional. The location of the PrivateDNSZone. Should be global." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "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')]", + "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": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privatednszone.{0}.{1}', replace('0.7.0', '.', '-'), 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": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the deployed SOA record." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the deployed SOA record." - }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/SOA', parameters('privateDnsZoneName'), parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group of the deployed SOA record." - }, - "value": "[resourceGroup().name]" + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" } } } + } + }, + "privateDnsZone": { + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]" + }, + "privateDnsZone_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/privateDnsZones/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" }, "dependsOn": [ "privateDnsZone" ] }, - "privateDnsZone_SRV": { + "privateDnsZone_roleAssignments": { "copy": { - "name": "privateDnsZone_SRV", - "count": "[length(coalesce(parameters('srv'), createArray()))]" + "name": "privateDnsZone_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones', 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": [ + "privateDnsZone" + ] + }, + "privateDnsZone_A": { + "copy": { + "name": "privateDnsZone_A", + "count": "[length(coalesce(parameters('a'), createArray()))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-SRVRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "name": "[format('{0}-PrivateDnsZone-ARecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -14344,19 +11717,19 @@ "value": "[parameters('name')]" }, "name": { - "value": "[coalesce(parameters('srv'), createArray())[copyIndex()].name]" + "value": "[coalesce(parameters('a'), createArray())[copyIndex()].name]" }, - "metadata": { - "value": "[tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'metadata')]" + "aRecords": { + "value": "[tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'aRecords')]" }, - "srvRecords": { - "value": "[tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'srvRecords')]" + "metadata": { + "value": "[tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'metadata')]" }, "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'ttl'), 3600)]" + "value": "[coalesce(tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'ttl'), 3600)]" }, "roleAssignments": { - "value": "[tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'roleAssignments')]" + "value": "[tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'roleAssignments')]" } }, "template": { @@ -14367,10 +11740,10 @@ "_generator": { "name": "bicep", "version": "0.32.4.45862", - "templateHash": "5790774778713328446" + "templateHash": "2531120132215940282" }, - "name": "Private DNS Zone SRV record", - "description": "This module deploys a Private DNS Zone SRV record.", + "name": "Private DNS Zone A record", + "description": "This module deploys a Private DNS Zone A record.", "owner": "Azure/module-maintainers" }, "definitions": { @@ -14458,21 +11831,21 @@ "name": { "type": "string", "metadata": { - "description": "Required. The name of the SRV record." + "description": "Required. The name of the A record." } }, - "metadata": { - "type": "object", + "aRecords": { + "type": "array", "nullable": true, "metadata": { - "description": "Optional. The metadata attached to the record set." + "description": "Optional. The list of A records in the record set." } }, - "srvRecords": { - "type": "array", + "metadata": { + "type": "object", "nullable": true, "metadata": { - "description": "Optional. The list of SRV records in the record set." + "description": "Optional. The metadata attached to the record set." } }, "ttl": { @@ -14514,25 +11887,25 @@ "apiVersion": "2020-06-01", "name": "[parameters('privateDnsZoneName')]" }, - "SRV": { - "type": "Microsoft.Network/privateDnsZones/SRV", + "A": { + "type": "Microsoft.Network/privateDnsZones/A", "apiVersion": "2020-06-01", "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", "properties": { + "aRecords": "[parameters('aRecords')]", "metadata": "[parameters('metadata')]", - "srvRecords": "[parameters('srvRecords')]", "ttl": "[parameters('ttl')]" } }, - "SRV_roleAssignments": { + "A_roleAssignments": { "copy": { - "name": "SRV_roleAssignments", + "name": "A_roleAssignments", "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/SRV/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/SRV', parameters('privateDnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/A/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/A', parameters('privateDnsZoneName'), 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]", @@ -14543,7 +11916,7 @@ "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" }, "dependsOn": [ - "SRV" + "A" ] } }, @@ -14551,21 +11924,21 @@ "name": { "type": "string", "metadata": { - "description": "The name of the deployed SRV record." + "description": "The name of the deployed A record." }, "value": "[parameters('name')]" }, "resourceId": { "type": "string", "metadata": { - "description": "The resource ID of the deployed SRV record." + "description": "The resource ID of the deployed A record." }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/SRV', parameters('privateDnsZoneName'), parameters('name'))]" + "value": "[resourceId('Microsoft.Network/privateDnsZones/A', parameters('privateDnsZoneName'), parameters('name'))]" }, "resourceGroupName": { "type": "string", "metadata": { - "description": "The resource group of the deployed SRV record." + "description": "The resource group of the deployed A record." }, "value": "[resourceGroup().name]" } @@ -14576,14 +11949,14 @@ "privateDnsZone" ] }, - "privateDnsZone_TXT": { + "privateDnsZone_AAAA": { "copy": { - "name": "privateDnsZone_TXT", - "count": "[length(coalesce(parameters('txt'), createArray()))]" + "name": "privateDnsZone_AAAA", + "count": "[length(coalesce(parameters('aaaa'), createArray()))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-TXTRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "name": "[format('{0}-PrivateDnsZone-AAAARecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -14594,19 +11967,19 @@ "value": "[parameters('name')]" }, "name": { - "value": "[coalesce(parameters('txt'), createArray())[copyIndex()].name]" + "value": "[coalesce(parameters('aaaa'), createArray())[copyIndex()].name]" }, - "metadata": { - "value": "[tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'metadata')]" + "aaaaRecords": { + "value": "[tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'aaaaRecords')]" }, - "txtRecords": { - "value": "[tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'txtRecords')]" + "metadata": { + "value": "[tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'metadata')]" }, "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'ttl'), 3600)]" + "value": "[coalesce(tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'ttl'), 3600)]" }, "roleAssignments": { - "value": "[tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'roleAssignments')]" + "value": "[tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'roleAssignments')]" } }, "template": { @@ -14617,10 +11990,10 @@ "_generator": { "name": "bicep", "version": "0.32.4.45862", - "templateHash": "1855369119498044639" + "templateHash": "16709340450244912125" }, - "name": "Private DNS Zone TXT record", - "description": "This module deploys a Private DNS Zone TXT record.", + "name": "Private DNS Zone AAAA record", + "description": "This module deploys a Private DNS Zone AAAA record.", "owner": "Azure/module-maintainers" }, "definitions": { @@ -14708,7 +12081,14 @@ "name": { "type": "string", "metadata": { - "description": "Required. The name of the TXT record." + "description": "Required. The name of the AAAA record." + } + }, + "aaaaRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of AAAA records in the record set." } }, "metadata": { @@ -14725,13 +12105,6 @@ "description": "Optional. The TTL (time-to-live) of the records in the record set." } }, - "txtRecords": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. The list of TXT records in the record set." - } - }, "roleAssignments": { "$ref": "#/definitions/roleAssignmentType", "metadata": { @@ -14764,25 +12137,25 @@ "apiVersion": "2020-06-01", "name": "[parameters('privateDnsZoneName')]" }, - "TXT": { - "type": "Microsoft.Network/privateDnsZones/TXT", + "AAAA": { + "type": "Microsoft.Network/privateDnsZones/AAAA", "apiVersion": "2020-06-01", "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", "properties": { + "aaaaRecords": "[parameters('aaaaRecords')]", "metadata": "[parameters('metadata')]", - "ttl": "[parameters('ttl')]", - "txtRecords": "[parameters('txtRecords')]" + "ttl": "[parameters('ttl')]" } }, - "TXT_roleAssignments": { + "AAAA_roleAssignments": { "copy": { - "name": "TXT_roleAssignments", + "name": "AAAA_roleAssignments", "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/TXT/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/TXT', parameters('privateDnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/AAAA/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/AAAA', parameters('privateDnsZoneName'), 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]", @@ -14793,7 +12166,7 @@ "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" }, "dependsOn": [ - "TXT" + "AAAA" ] } }, @@ -14801,21 +12174,21 @@ "name": { "type": "string", "metadata": { - "description": "The name of the deployed TXT record." + "description": "The name of the deployed AAAA record." }, "value": "[parameters('name')]" }, "resourceId": { "type": "string", "metadata": { - "description": "The resource ID of the deployed TXT record." + "description": "The resource ID of the deployed AAAA record." }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/TXT', parameters('privateDnsZoneName'), parameters('name'))]" + "value": "[resourceId('Microsoft.Network/privateDnsZones/AAAA', parameters('privateDnsZoneName'), parameters('name'))]" }, "resourceGroupName": { "type": "string", "metadata": { - "description": "The resource group of the deployed TXT record." + "description": "The resource group of the deployed AAAA record." }, "value": "[resourceGroup().name]" } @@ -14826,14 +12199,14 @@ "privateDnsZone" ] }, - "privateDnsZone_virtualNetworkLinks": { + "privateDnsZone_CNAME": { "copy": { - "name": "privateDnsZone_virtualNetworkLinks", - "count": "[length(coalesce(parameters('virtualNetworkLinks'), createArray()))]" + "name": "privateDnsZone_CNAME", + "count": "[length(coalesce(parameters('cname'), createArray()))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-VirtualNetworkLink-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "name": "[format('{0}-PrivateDnsZone-CNAMERecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -14844,22 +12217,19 @@ "value": "[parameters('name')]" }, "name": { - "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'name'), format('{0}-vnetlink', last(split(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()].virtualNetworkResourceId, '/'))))]" - }, - "virtualNetworkResourceId": { - "value": "[coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()].virtualNetworkResourceId]" + "value": "[coalesce(parameters('cname'), createArray())[copyIndex()].name]" }, - "location": { - "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'location'), 'global')]" + "cnameRecord": { + "value": "[tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'cnameRecord')]" }, - "registrationEnabled": { - "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'registrationEnabled'), false())]" + "metadata": { + "value": "[tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'metadata')]" }, - "tags": { - "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'ttl'), 3600)]" }, - "resolutionPolicy": { - "value": "[tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'resolutionPolicy')]" + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'roleAssignments')]" } }, "template": { @@ -14870,12 +12240,87 @@ "_generator": { "name": "bicep", "version": "0.32.4.45862", - "templateHash": "15326596012552051215" + "templateHash": "9976020649752073181" }, - "name": "Private DNS Zone Virtual Network Link", - "description": "This module deploys a Private DNS Zone Virtual Network Link.", + "name": "Private DNS Zone CNAME record", + "description": "This module deploys a Private DNS Zone CNAME record.", "owner": "Azure/module-maintainers" }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "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." + } + } + } + }, + "nullable": true + } + }, "parameters": { "privateDnsZoneName": { "type": "string", @@ -14885,44 +12330,54 @@ }, "name": { "type": "string", - "defaultValue": "[format('{0}-vnetlink', last(split(parameters('virtualNetworkResourceId'), '/')))]", "metadata": { - "description": "Optional. The name of the virtual network link." + "description": "Required. The name of the CNAME record." } }, - "location": { - "type": "string", - "defaultValue": "global", + "cnameRecord": { + "type": "object", + "nullable": true, "metadata": { - "description": "Optional. The location of the PrivateDNSZone. Should be global." + "description": "Optional. A CNAME record." } }, - "tags": { + "metadata": { "type": "object", "nullable": true, "metadata": { - "description": "Optional. Tags of the resource." + "description": "Optional. The metadata attached to the record set." } }, - "registrationEnabled": { - "type": "bool", - "defaultValue": false, + "ttl": { + "type": "int", + "defaultValue": 3600, "metadata": { - "description": "Optional. Is auto-registration of virtual machine records in the virtual network in the Private DNS zone enabled?." + "description": "Optional. The TTL (time-to-live) of the records in the record set." } }, - "virtualNetworkResourceId": { - "type": "string", + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", "metadata": { - "description": "Required. Link to another virtual network resource ID." + "description": "Optional. Array of role assignments to create." } - }, - "resolutionPolicy": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The resolution policy on the virtual network link. Only applicable for virtual network links to privatelink zones, and for A,AAAA,CNAME queries. When set to `NxDomainRedirect`, Azure DNS resolver falls back to public resolution if private dns query resolution results in non-existent domain response. `Default` is configured as the default option." + } + }, + "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')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, "resources": { @@ -14932,49 +12387,60 @@ "apiVersion": "2020-06-01", "name": "[parameters('privateDnsZoneName')]" }, - "virtualNetworkLink": { - "type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks", - "apiVersion": "2024-06-01", + "CNAME": { + "type": "Microsoft.Network/privateDnsZones/CNAME", + "apiVersion": "2020-06-01", "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", "properties": { - "registrationEnabled": "[parameters('registrationEnabled')]", - "virtualNetwork": { - "id": "[parameters('virtualNetworkResourceId')]" - }, - "resolutionPolicy": "[parameters('resolutionPolicy')]" + "cnameRecord": "[parameters('cnameRecord')]", + "metadata": "[parameters('metadata')]", + "ttl": "[parameters('ttl')]" } + }, + "CNAME_roleAssignments": { + "copy": { + "name": "CNAME_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/CNAME/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/CNAME', parameters('privateDnsZoneName'), 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": [ + "CNAME" + ] } }, "outputs": { "name": { "type": "string", "metadata": { - "description": "The name of the deployed virtual network link." + "description": "The name of the deployed CNAME record." }, "value": "[parameters('name')]" }, "resourceId": { "type": "string", "metadata": { - "description": "The resource ID of the deployed virtual network link." + "description": "The resource ID of the deployed CNAME record." }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/virtualNetworkLinks', parameters('privateDnsZoneName'), parameters('name'))]" + "value": "[resourceId('Microsoft.Network/privateDnsZones/CNAME', parameters('privateDnsZoneName'), parameters('name'))]" }, "resourceGroupName": { "type": "string", "metadata": { - "description": "The resource group of the deployed virtual network link." + "description": "The resource group of the deployed CNAME record." }, "value": "[resourceGroup().name]" - }, - "location": { - "type": "string", - "metadata": { - "description": "The location the resource was deployed into." - }, - "value": "[reference('virtualNetworkLink', '2024-06-01', 'full').location]" } } } @@ -14982,2560 +12448,3213 @@ "dependsOn": [ "privateDnsZone" ] - } - }, - "outputs": { - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group the private DNS zone was deployed into." - }, - "value": "[resourceGroup().name]" - }, - "name": { - "type": "string", - "metadata": { - "description": "The name of the private DNS zone." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the private DNS zone." - }, - "value": "[resourceId('Microsoft.Network/privateDnsZones', parameters('name'))]" }, - "location": { - "type": "string", - "metadata": { - "description": "The location the resource was deployed into." + "privateDnsZone_MX": { + "copy": { + "name": "privateDnsZone_MX", + "count": "[length(coalesce(parameters('mx'), createArray()))]" }, - "value": "[reference('privateDnsZone', '2020-06-01', 'full').location]" - } - } - } - } - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[take(format('{0}-container-registry-deployment', variables('nameFormatted')), 64)]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[variables('nameFormatted')]" - }, - "location": { - "value": "[parameters('location')]" - }, - "tags": { - "value": "[parameters('tags')]" - }, - "acrSku": { - "value": "Premium" - }, - "acrAdminUserEnabled": { - "value": false - }, - "anonymousPullEnabled": { - "value": false - }, - "dataEndpointEnabled": { - "value": false - }, - "networkRuleBypassOptions": { - "value": "AzureServices" - }, - "networkRuleSetDefaultAction": "[if(parameters('networkIsolation'), createObject('value', 'Deny'), createObject('value', 'Allow'))]", - "exportPolicyStatus": "[if(parameters('networkIsolation'), createObject('value', 'disabled'), createObject('value', 'enabled'))]", - "publicNetworkAccess": "[if(parameters('networkIsolation'), createObject('value', 'Disabled'), createObject('value', 'Enabled'))]", - "zoneRedundancy": { - "value": "Disabled" - }, - "managedIdentities": { - "value": { - "systemAssigned": true - } - }, - "diagnosticSettings": { - "value": [ - { - "workspaceResourceId": "[parameters('logAnalyticsWorkspaceResourceId')]" - } - ] - }, - "privateEndpoints": "[if(parameters('networkIsolation'), createObject('value', createArray(createObject('privateDnsZoneGroup', createObject('privateDnsZoneGroupConfigs', createArray(createObject('privateDnsZoneResourceId', reference(resourceId('Microsoft.Resources/deployments', 'private-dns-acr-deployment'), '2022-09-01').outputs.resourceId.value))), 'subnetResourceId', parameters('virtualNetworkSubnetResourceId')))), createObject('value', createArray()))]" - }, - "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.33.93.31351", - "templateHash": "350214817154408151" - }, - "name": "Azure Container Registries (ACR)", - "description": "This module deploys an Azure Container Registry (ACR)." - }, - "definitions": { - "privateEndpointOutputType": { - "type": "object", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-MXRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the private endpoint." - } + "expressionEvaluationOptions": { + "scope": "inner" }, - "resourceId": { - "type": "string", + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('mx'), createArray())[copyIndex()].name]" + }, "metadata": { - "description": "The resource ID of the private endpoint." + "value": "[tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'metadata')]" + }, + "mxRecords": { + "value": "[tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'mxRecords')]" + }, + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'ttl'), 3600)]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'roleAssignments')]" } }, - "groupId": { - "type": "string", - "nullable": true, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", "metadata": { - "description": "The group Id for the private endpoint Group." - } - }, - "customDnsConfigs": { - "type": "array", - "items": { - "type": "object", - "properties": { - "fqdn": { - "type": "string", - "nullable": true, - "metadata": { - "description": "FQDN that resolves to private endpoint IP address." + "_generator": { + "name": "bicep", + "version": "0.32.4.45862", + "templateHash": "2520323624213076361" + }, + "name": "Private DNS Zone MX record", + "description": "This module deploys a Private DNS Zone MX record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "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." + } + } } }, - "ipAddresses": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "A list of private IP addresses of the private endpoint." - } + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the MX record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "mxRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of MX records in the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." } } }, - "metadata": { - "description": "The custom DNS configurations of the private endpoint." - } - }, - "networkInterfaceResourceIds": { - "type": "array", - "items": { - "type": "string" + "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')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } }, - "metadata": { - "description": "The IDs of the network interfaces associated with the private endpoint." - } - } - }, - "metadata": { - "__bicep_export!": true - } - }, - "scopeMapsType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the scope map." - } - }, - "actions": { - "type": "array", - "items": { - "type": "string" + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "MX": { + "type": "Microsoft.Network/privateDnsZones/MX", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "properties": { + "metadata": "[parameters('metadata')]", + "mxRecords": "[parameters('mxRecords')]", + "ttl": "[parameters('ttl')]" + } + }, + "MX_roleAssignments": { + "copy": { + "name": "MX_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/MX/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/MX', parameters('privateDnsZoneName'), 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": [ + "MX" + ] + } }, - "metadata": { - "description": "Required. The list of scoped permissions for registry artifacts." - } - }, - "description": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The user friendly description of the scope map." + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed MX record." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed MX record." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/MX', parameters('privateDnsZoneName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed MX record." + }, + "value": "[resourceGroup().name]" + } } } }, - "metadata": { - "__bicep_export!": true, - "description": "The type for a scope map." - } + "dependsOn": [ + "privateDnsZone" + ] }, - "cacheRuleType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the cache rule. Will be derived from the source repository name if not defined." - } - }, - "sourceRepository": { - "type": "string", - "metadata": { - "description": "Required. Source repository pulled from upstream." - } - }, - "targetRepository": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Target repository specified in docker pull command. E.g.: docker pull myregistry.azurecr.io/{targetRepository}:{tag}." - } - }, - "credentialSetResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The resource ID of the credential store which is associated with the cache rule." - } - } + "privateDnsZone_PTR": { + "copy": { + "name": "privateDnsZone_PTR", + "count": "[length(coalesce(parameters('ptr'), createArray()))]" }, - "metadata": { - "__bicep_export!": true, - "description": "The type for a cache rule." - } - }, - "credentialSetType": { - "type": "object", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-PTRRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the credential set." - } - }, - "managedIdentities": { - "$ref": "#/definitions/managedIdentityOnlySysAssignedType", - "nullable": true, - "metadata": { - "description": "Optional. The managed identity definition for this resource." - } + "expressionEvaluationOptions": { + "scope": "inner" }, - "authCredentials": { - "type": "array", - "items": { - "$ref": "#/definitions/authCredentialsType" + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" }, - "metadata": { - "description": "Required. List of authentication credentials stored for an upstream. Usually consists of a primary and an optional secondary credential." - } - }, - "loginServer": { - "type": "string", - "metadata": { - "description": "Required. The credentials are stored for this upstream or login server." - } - } - }, - "metadata": { - "__bicep_export!": true, - "description": "The type for a credential set." - } - }, - "replicationType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the replication." - } - }, - "location": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Location for all resources." - } - }, - "tags": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Tags of the resource." - } - }, - "regionEndpointEnabled": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Specifies whether the replication regional endpoint is enabled. Requests will not be routed to a replication whose regional endpoint is disabled, however its data will continue to be synced with other replications." - } - }, - "zoneRedundancy": { - "type": "string", - "allowedValues": [ - "Disabled", - "Enabled" - ], - "nullable": true, - "metadata": { - "description": "Optional. Whether or not zone redundancy is enabled for this container registry." - } - } - }, - "metadata": { - "__bicep_export!": true, - "description": "The type for a replication." - } - }, - "webhookType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "minLength": 5, - "maxLength": 50, - "metadata": { - "description": "Optional. The name of the registry webhook." - } - }, - "serviceUri": { - "type": "string", - "metadata": { - "description": "Required. The service URI for the webhook to post notifications." - } - }, - "status": { - "type": "string", - "allowedValues": [ - "disabled", - "enabled" - ], - "nullable": true, - "metadata": { - "description": "Optional. The status of the webhook at the time the operation was called." - } - }, - "action": { - "type": "array", - "items": { - "type": "string" + "name": { + "value": "[coalesce(parameters('ptr'), createArray())[copyIndex()].name]" }, - "nullable": true, - "metadata": { - "description": "Optional. The list of actions that trigger the webhook to post notifications." - } - }, - "location": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Location for all resources." - } - }, - "tags": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Tags of the resource." - } - }, - "customHeaders": { - "type": "object", - "nullable": true, "metadata": { - "description": "Optional. Custom headers that will be added to the webhook notifications." + "value": "[tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'metadata')]" + }, + "ptrRecords": { + "value": "[tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'ptrRecords')]" + }, + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'ttl'), 3600)]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'roleAssignments')]" } }, - "scope": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The scope of repositories where the event can be triggered. For example, 'foo:*' means events for all tags under repository 'foo'. 'foo:bar' means events for 'foo:bar' only. 'foo' is equivalent to 'foo:latest'. Empty means all events." - } - } - }, - "metadata": { - "__bicep_export!": true, - "description": "The type for a webhook." - } - }, - "_1.privateEndpointCustomDnsConfigType": { - "type": "object", - "properties": { - "fqdn": { - "type": "string", - "nullable": true, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", "metadata": { - "description": "Optional. FQDN that resolves to private endpoint IP address." - } - }, - "ipAddresses": { - "type": "array", - "items": { - "type": "string" + "_generator": { + "name": "bicep", + "version": "0.32.4.45862", + "templateHash": "3080404733048745471" + }, + "name": "Private DNS Zone PTR record", + "description": "This module deploys a Private DNS Zone PTR record.", + "owner": "Azure/module-maintainers" }, - "metadata": { - "description": "Required. A list of private IP addresses of the private endpoint." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } - } - }, - "_1.privateEndpointIpConfigurationType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the resource that is unique within a resource group." - } - }, - "properties": { - "type": "object", - "properties": { - "groupId": { + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "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." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { "type": "string", "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." } }, - "memberName": { + "name": { "type": "string", "metadata": { - "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to." + "description": "Required. The name of the PTR record." } }, - "privateIPAddress": { - "type": "string", + "metadata": { + "type": "object", + "nullable": true, "metadata": { - "description": "Required. A private IP address obtained from the private endpoint's subnet." + "description": "Optional. The metadata attached to the record set." + } + }, + "ptrRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of PTR records in the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." } } }, - "metadata": { - "description": "Required. Properties of private endpoint IP configurations." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } - } - }, - "_1.privateEndpointPrivateDnsZoneGroupType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the Private DNS Zone Group." - } - }, - "privateDnsZoneGroupConfigs": { - "type": "array", - "items": { - "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." - } + "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')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, - "metadata": { - "description": "Required. The private DNS Zone Groups to associate the Private Endpoint. A DNS Zone Group can support up to 5 DNS zones." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } - } - }, - "authCredentialsType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the credential." - } - }, - "usernameSecretIdentifier": { - "type": "string", - "metadata": { - "description": "Required. KeyVault Secret URI for accessing the username." - } - }, - "passwordSecretIdentifier": { - "type": "string", - "metadata": { - "description": "Required. KeyVault Secret URI for accessing the password." + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "PTR": { + "type": "Microsoft.Network/privateDnsZones/PTR", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "properties": { + "metadata": "[parameters('metadata')]", + "ptrRecords": "[parameters('ptrRecords')]", + "ttl": "[parameters('ttl')]" + } + }, + "PTR_roleAssignments": { + "copy": { + "name": "PTR_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/PTR/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/PTR', parameters('privateDnsZoneName'), 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": [ + "PTR" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed PTR record." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed PTR record." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/PTR', parameters('privateDnsZoneName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed PTR record." + }, + "value": "[resourceGroup().name]" + } } } }, - "metadata": { - "description": "The type for auth credentials.", - "__bicep_imported_from!": { - "sourceTemplate": "credential-set/main.bicep" - } - } + "dependsOn": [ + "privateDnsZone" + ] }, - "customerManagedKeyWithAutoRotateType": { - "type": "object", + "privateDnsZone_SOA": { + "copy": { + "name": "privateDnsZone_SOA", + "count": "[length(coalesce(parameters('soa'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-SOARecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { - "keyVaultResourceId": { - "type": "string", - "metadata": { - "description": "Required. The resource ID of a key vault to reference a customer managed key for encryption from." - } - }, - "keyName": { - "type": "string", - "metadata": { - "description": "Required. The name of the customer managed key to use for encryption." - } - }, - "keyVersion": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The version of the customer managed key to reference for encryption. If not provided, using version as per 'autoRotationEnabled' setting." - } + "expressionEvaluationOptions": { + "scope": "inner" }, - "autoRotationEnabled": { - "type": "bool", - "nullable": true, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('soa'), createArray())[copyIndex()].name]" + }, "metadata": { - "description": "Optional. Enable or disable auto-rotating to the latest key version. Default is `true`. If set to `false`, the latest key version at the time of the deployment is used." + "value": "[tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'metadata')]" + }, + "soaRecord": { + "value": "[tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'soaRecord')]" + }, + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'ttl'), 3600)]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'roleAssignments')]" } }, - "userAssignedIdentityResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use." - } - } - }, - "metadata": { - "description": "An AVM-aligned type for a customer-managed key. To be used if the resource type supports auto-rotation of the customer-managed key.", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } - } - }, - "diagnosticSettingFullType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", "metadata": { - "description": "Optional. The name of the diagnostic setting." - } - }, - "logCategoriesAndGroups": { - "type": "array", - "items": { - "type": "object", - "properties": { - "category": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." - } - }, - "categoryGroup": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + "_generator": { + "name": "bicep", + "version": "0.32.4.45862", + "templateHash": "6653951445614700931" + }, + "name": "Private DNS Zone SOA record", + "description": "This module deploys a Private DNS Zone SOA record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "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." + } + } } }, - "enabled": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Enable or disable the category explicitly. Default is `true`." - } + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the SOA record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "soaRecord": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. A SOA record." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." } } }, - "nullable": true, - "metadata": { - "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." - } - }, - "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`." - } + "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')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, - "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." + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "SOA": { + "type": "Microsoft.Network/privateDnsZones/SOA", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "properties": { + "metadata": "[parameters('metadata')]", + "soaRecord": "[parameters('soaRecord')]", + "ttl": "[parameters('ttl')]" + } + }, + "SOA_roleAssignments": { + "copy": { + "name": "SOA_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/SOA/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/SOA', parameters('privateDnsZoneName'), 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": [ + "SOA" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed SOA record." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed SOA record." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/SOA', parameters('privateDnsZoneName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed SOA record." + }, + "value": "[resourceGroup().name]" + } } } }, - "metadata": { - "description": "An AVM-aligned type for a diagnostic setting. To be used if both logs & metrics are supported by the resource provider.", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } - } + "dependsOn": [ + "privateDnsZone" + ] }, - "lockType": { - "type": "object", + "privateDnsZone_SRV": { + "copy": { + "name": "privateDnsZone_SRV", + "count": "[length(coalesce(parameters('srv'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-SRVRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { - "name": { - "type": "string", - "nullable": true, + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('srv'), createArray())[copyIndex()].name]" + }, "metadata": { - "description": "Optional. Specify the name of lock." + "value": "[tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'metadata')]" + }, + "srvRecords": { + "value": "[tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'srvRecords')]" + }, + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'ttl'), 3600)]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'roleAssignments')]" } }, - "kind": { - "type": "string", - "allowedValues": [ - "CanNotDelete", - "None", - "ReadOnly" - ], - "nullable": true, - "metadata": { - "description": "Optional. Specify the type of 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.5.1" - } - } - }, - "managedIdentityAllType": { - "type": "object", - "properties": { - "systemAssigned": { - "type": "bool", - "nullable": true, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", "metadata": { - "description": "Optional. Enables system assigned managed identity on the resource." - } - }, - "userAssignedResourceIds": { - "type": "array", - "items": { - "type": "string" + "_generator": { + "name": "bicep", + "version": "0.32.4.45862", + "templateHash": "5790774778713328446" + }, + "name": "Private DNS Zone SRV record", + "description": "This module deploys a Private DNS Zone SRV record.", + "owner": "Azure/module-maintainers" }, - "nullable": true, - "metadata": { - "description": "Optional. The resource ID(s) to assign to the resource. Required if a user assigned identity is used for encryption." + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "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." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the SRV record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "srvRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of SRV records in the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "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')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "SRV": { + "type": "Microsoft.Network/privateDnsZones/SRV", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "properties": { + "metadata": "[parameters('metadata')]", + "srvRecords": "[parameters('srvRecords')]", + "ttl": "[parameters('ttl')]" + } + }, + "SRV_roleAssignments": { + "copy": { + "name": "SRV_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/SRV/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/SRV', parameters('privateDnsZoneName'), 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": [ + "SRV" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed SRV record." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed SRV record." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/SRV', parameters('privateDnsZoneName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed SRV record." + }, + "value": "[resourceGroup().name]" + } } } }, - "metadata": { - "description": "An AVM-aligned type for a managed identity configuration. To be used if both a system-assigned & user-assigned identities are supported by the resource provider.", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } - } + "dependsOn": [ + "privateDnsZone" + ] }, - "managedIdentityOnlySysAssignedType": { - "type": "object", - "properties": { - "systemAssigned": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Enables system assigned managed identity on the resource." - } - } + "privateDnsZone_TXT": { + "copy": { + "name": "privateDnsZone_TXT", + "count": "[length(coalesce(parameters('txt'), createArray()))]" }, - "metadata": { - "description": "An AVM-aligned type for a managed identity configuration. To be used if only system-assigned identities are supported by the resource provider.", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } - } - }, - "privateEndpointSingleServiceType": { - "type": "object", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-TXTRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the Private Endpoint." - } - }, - "location": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The location to deploy the Private Endpoint to." - } - }, - "privateLinkServiceConnectionName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the private link connection to create." - } - }, - "service": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The subresource to deploy the Private Endpoint for. For example \"vault\" for a Key Vault Private Endpoint." - } - }, - "subnetResourceId": { - "type": "string", - "metadata": { - "description": "Required. Resource ID of the subnet where the endpoint needs to be created." - } - }, - "resourceGroupResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The resource ID of the Resource Group the Private Endpoint will be created in. If not specified, the Resource Group of the provided Virtual Network Subnet is used." - } - }, - "privateDnsZoneGroup": { - "$ref": "#/definitions/_1.privateEndpointPrivateDnsZoneGroupType", - "nullable": true, - "metadata": { - "description": "Optional. The private DNS Zone Group to configure for the Private Endpoint." - } - }, - "isManualConnection": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. If Manual Private Link Connection is required." - } - }, - "manualConnectionRequestMessage": { - "type": "string", - "nullable": true, - "maxLength": 140, - "metadata": { - "description": "Optional. A message passed to the owner of the remote resource with the manual connection request." - } + "expressionEvaluationOptions": { + "scope": "inner" }, - "customDnsConfigs": { - "type": "array", - "items": { - "$ref": "#/definitions/_1.privateEndpointCustomDnsConfigType" + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" }, - "nullable": true, - "metadata": { - "description": "Optional. Custom DNS configurations." - } - }, - "ipConfigurations": { - "type": "array", - "items": { - "$ref": "#/definitions/_1.privateEndpointIpConfigurationType" + "name": { + "value": "[coalesce(parameters('txt'), createArray())[copyIndex()].name]" }, - "nullable": true, "metadata": { - "description": "Optional. A list of IP configurations of the Private Endpoint. This will be used to map to the first-party Service endpoints." - } - }, - "applicationSecurityGroupResourceIds": { - "type": "array", - "items": { - "type": "string" + "value": "[tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'metadata')]" }, - "nullable": true, - "metadata": { - "description": "Optional. Application security groups in which the Private Endpoint IP configuration is included." - } - }, - "customNetworkInterfaceName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The custom name of the network interface attached to the Private Endpoint." - } - }, - "lock": { - "$ref": "#/definitions/lockType", - "nullable": true, - "metadata": { - "description": "Optional. Specify the type of lock." - } - }, - "roleAssignments": { - "type": "array", - "items": { - "$ref": "#/definitions/roleAssignmentType" + "txtRecords": { + "value": "[tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'txtRecords')]" }, - "nullable": true, - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "tags": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Tags to be applied on all resources/Resource Groups in this deployment." - } - }, - "enableTelemetry": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Enable/Disable usage telemetry for module." - } - } - }, - "metadata": { - "description": "An AVM-aligned type for a private endpoint. To be used if the private endpoint's default service / groupId can be assumed (i.e., for services that only have one Private Endpoint type like 'vault' for key vault).", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } - } - }, - "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." + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'ttl'), 3600)]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'roleAssignments')]" } }, - "delegatedManagedIdentityResourceId": { - "type": "string", - "nullable": true, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", "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.5.1" - } - } - } - }, - "parameters": { - "name": { - "type": "string", - "minLength": 5, - "maxLength": 50, - "metadata": { - "description": "Required. Name of your Azure Container Registry." - } - }, - "acrAdminUserEnabled": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "Optional. Enable admin user that have push / pull permission to the registry." - } - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]", - "metadata": { - "description": "Optional. Location for all resources." - } - }, - "roleAssignments": { - "type": "array", - "items": { - "$ref": "#/definitions/roleAssignmentType" + "_generator": { + "name": "bicep", + "version": "0.32.4.45862", + "templateHash": "1855369119498044639" + }, + "name": "Private DNS Zone TXT record", + "description": "This module deploys a Private DNS Zone TXT record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "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." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the TXT record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "txtRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of TXT records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "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')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "TXT": { + "type": "Microsoft.Network/privateDnsZones/TXT", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "properties": { + "metadata": "[parameters('metadata')]", + "ttl": "[parameters('ttl')]", + "txtRecords": "[parameters('txtRecords')]" + } + }, + "TXT_roleAssignments": { + "copy": { + "name": "TXT_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/TXT/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/TXT', parameters('privateDnsZoneName'), 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": [ + "TXT" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed TXT record." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed TXT record." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/TXT', parameters('privateDnsZoneName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed TXT record." + }, + "value": "[resourceGroup().name]" + } + } + } }, - "nullable": true, - "metadata": { - "description": "Optional. Array of role assignments to create." - } + "dependsOn": [ + "privateDnsZone" + ] }, - "acrSku": { + "privateDnsZone_virtualNetworkLinks": { + "copy": { + "name": "privateDnsZone_virtualNetworkLinks", + "count": "[length(coalesce(parameters('virtualNetworkLinks'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-VirtualNetworkLink-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'name'), format('{0}-vnetlink', last(split(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()].virtualNetworkResourceId, '/'))))]" + }, + "virtualNetworkResourceId": { + "value": "[coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()].virtualNetworkResourceId]" + }, + "location": { + "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'location'), 'global')]" + }, + "registrationEnabled": { + "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'registrationEnabled'), false())]" + }, + "tags": { + "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + }, + "resolutionPolicy": { + "value": "[tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'resolutionPolicy')]" + } + }, + "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.32.4.45862", + "templateHash": "15326596012552051215" + }, + "name": "Private DNS Zone Virtual Network Link", + "description": "This module deploys a Private DNS Zone Virtual Network Link.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "defaultValue": "[format('{0}-vnetlink', last(split(parameters('virtualNetworkResourceId'), '/')))]", + "metadata": { + "description": "Optional. The name of the virtual network link." + } + }, + "location": { + "type": "string", + "defaultValue": "global", + "metadata": { + "description": "Optional. The location of the PrivateDNSZone. Should be global." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "registrationEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Is auto-registration of virtual machine records in the virtual network in the Private DNS zone enabled?." + } + }, + "virtualNetworkResourceId": { + "type": "string", + "metadata": { + "description": "Required. Link to another virtual network resource ID." + } + }, + "resolutionPolicy": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resolution policy on the virtual network link. Only applicable for virtual network links to privatelink zones, and for A,AAAA,CNAME queries. When set to `NxDomainRedirect`, Azure DNS resolver falls back to public resolution if private dns query resolution results in non-existent domain response. `Default` is configured as the default option." + } + } + }, + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "virtualNetworkLink": { + "type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks", + "apiVersion": "2024-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "registrationEnabled": "[parameters('registrationEnabled')]", + "virtualNetwork": { + "id": "[parameters('virtualNetworkResourceId')]" + }, + "resolutionPolicy": "[parameters('resolutionPolicy')]" + } + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed virtual network link." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed virtual network link." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/virtualNetworkLinks', parameters('privateDnsZoneName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed virtual network link." + }, + "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('virtualNetworkLink', '2024-06-01', 'full').location]" + } + } + } + }, + "dependsOn": [ + "privateDnsZone" + ] + } + }, + "outputs": { + "resourceGroupName": { "type": "string", - "defaultValue": "Premium", - "allowedValues": [ - "Basic", - "Premium", - "Standard" - ], "metadata": { - "description": "Optional. Tier of your Azure container registry." - } + "description": "The resource group the private DNS zone was deployed into." + }, + "value": "[resourceGroup().name]" }, - "exportPolicyStatus": { + "name": { "type": "string", - "defaultValue": "disabled", - "allowedValues": [ - "disabled", - "enabled" - ], "metadata": { - "description": "Optional. The value that indicates whether the export policy is enabled or not." - } + "description": "The name of the private DNS zone." + }, + "value": "[parameters('name')]" }, - "quarantinePolicyStatus": { + "resourceId": { "type": "string", - "defaultValue": "disabled", - "allowedValues": [ - "disabled", - "enabled" - ], "metadata": { - "description": "Optional. The value that indicates whether the quarantine policy is enabled or not. Note, requires the 'acrSku' to be 'Premium'." - } + "description": "The resource ID of the private DNS zone." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones', parameters('name'))]" }, - "trustPolicyStatus": { + "location": { "type": "string", - "defaultValue": "disabled", - "allowedValues": [ - "disabled", - "enabled" - ], "metadata": { - "description": "Optional. The value that indicates whether the trust policy is enabled or not. Note, requires the 'acrSku' to be 'Premium'." + "description": "The location the resource was deployed into." + }, + "value": "[reference('privateDnsZone', '2020-06-01', 'full').location]" + } + } + } + } + }, + "keyvault": { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('{0}-keyvault-deployment', variables('nameFormatted')), 64)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[variables('nameFormatted')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "publicNetworkAccess": "[if(parameters('networkIsolation'), createObject('value', 'Disabled'), createObject('value', 'Enabled'))]", + "networkAcls": { + "value": { + "defaultAction": "Allow" + } + }, + "enableVaultForDeployment": { + "value": true + }, + "enableVaultForDiskEncryption": { + "value": true + }, + "enableVaultForTemplateDeployment": { + "value": true + }, + "enablePurgeProtection": { + "value": false + }, + "enableRbacAuthorization": { + "value": true + }, + "enableSoftDelete": { + "value": true + }, + "softDeleteRetentionInDays": { + "value": 7 + }, + "diagnosticSettings": { + "value": [ + { + "workspaceResourceId": "[parameters('logAnalyticsWorkspaceResourceId')]" } + ] + }, + "privateEndpoints": "[if(parameters('networkIsolation'), createObject('value', createArray(createObject('privateDnsZoneGroup', createObject('privateDnsZoneGroupConfigs', createArray(createObject('privateDnsZoneResourceId', reference('privateDnsZone').outputs.resourceId.value))), 'service', 'vault', 'subnetResourceId', parameters('virtualNetworkSubnetResourceId')))), createObject('value', createArray()))]", + "roleAssignments": { + "value": "[parameters('roleAssignments')]" + }, + "secrets": { + "value": "[parameters('secrets')]" + } + }, + "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.33.93.31351", + "templateHash": "17553975707245390963" }, - "retentionPolicyStatus": { - "type": "string", - "defaultValue": "enabled", - "allowedValues": [ - "disabled", - "enabled" - ], - "metadata": { - "description": "Optional. The value that indicates whether the retention policy is enabled or not." - } - }, - "retentionPolicyDays": { - "type": "int", - "defaultValue": 15, - "metadata": { - "description": "Optional. The number of days to retain an untagged manifest after which it gets purged." - } - }, - "azureADAuthenticationAsArmPolicyStatus": { - "type": "string", - "defaultValue": "enabled", - "allowedValues": [ - "disabled", - "enabled" - ], - "metadata": { - "description": "Optional. The value that indicates whether the policy for using ARM audience token for a container registry is enabled or not. Default is enabled." - } - }, - "softDeletePolicyStatus": { - "type": "string", - "defaultValue": "disabled", - "allowedValues": [ - "disabled", - "enabled" - ], - "metadata": { - "description": "Optional. Soft Delete policy status. Default is disabled." - } - }, - "softDeletePolicyDays": { - "type": "int", - "defaultValue": 7, - "metadata": { - "description": "Optional. The number of days after which a soft-deleted item is permanently deleted." - } - }, - "dataEndpointEnabled": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "Optional. Enable a single data endpoint per region for serving data. Not relevant in case of disabled public access. Note, requires the 'acrSku' to be 'Premium'." - } - }, - "publicNetworkAccess": { - "type": "string", - "nullable": true, - "allowedValues": [ - "Enabled", - "Disabled" - ], - "metadata": { - "description": "Optional. Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set and networkRuleSetIpRules are not set. Note, requires the 'acrSku' to be 'Premium'." - } - }, - "networkRuleBypassOptions": { - "type": "string", - "defaultValue": "AzureServices", - "allowedValues": [ - "AzureServices", - "None" - ], - "metadata": { - "description": "Optional. Whether to allow trusted Azure services to access a network restricted registry." - } - }, - "networkRuleSetDefaultAction": { - "type": "string", - "defaultValue": "Deny", - "allowedValues": [ - "Allow", - "Deny" - ], - "metadata": { - "description": "Optional. The default action of allow or deny when no other rules match." - } - }, - "networkRuleSetIpRules": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. The IP ACL rules. Note, requires the 'acrSku' to be 'Premium'." - } - }, - "privateEndpoints": { - "type": "array", - "items": { - "$ref": "#/definitions/privateEndpointSingleServiceType" - }, - "nullable": true, - "metadata": { - "description": "Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible. Note, requires the 'acrSku' to be 'Premium'." - } - }, - "zoneRedundancy": { - "type": "string", - "defaultValue": "Enabled", - "allowedValues": [ - "Disabled", - "Enabled" - ], - "metadata": { - "description": "Optional. Whether or not zone redundancy is enabled for this container registry." - } - }, - "replications": { - "type": "array", - "items": { - "$ref": "#/definitions/replicationType" - }, - "nullable": true, - "metadata": { - "description": "Optional. All replications to create." - } - }, - "webhooks": { - "type": "array", - "items": { - "$ref": "#/definitions/webhookType" - }, - "nullable": true, - "metadata": { - "description": "Optional. All webhooks to create." - } - }, - "lock": { - "$ref": "#/definitions/lockType", - "nullable": true, - "metadata": { - "description": "Optional. The lock settings of the service." - } - }, - "managedIdentities": { - "$ref": "#/definitions/managedIdentityAllType", - "nullable": true, - "metadata": { - "description": "Optional. The managed identity definition for this resource." - } - }, - "tags": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Tags of the resource." - } - }, - "enableTelemetry": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Optional. Enable/Disable usage telemetry for module." - } - }, - "diagnosticSettings": { - "type": "array", - "items": { - "$ref": "#/definitions/diagnosticSettingFullType" - }, - "nullable": true, - "metadata": { - "description": "Optional. The diagnostic settings of the service." - } - }, - "anonymousPullEnabled": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "Optional. Enables registry-wide pull from unauthenticated clients. It's in preview and available in the Standard and Premium service tiers." - } - }, - "customerManagedKey": { - "$ref": "#/definitions/customerManagedKeyWithAutoRotateType", - "nullable": true, - "metadata": { - "description": "Optional. The customer managed key definition." - } - }, - "cacheRules": { - "type": "array", - "items": { - "$ref": "#/definitions/cacheRuleType" - }, - "nullable": true, - "metadata": { - "description": "Optional. Array of Cache Rules." - } - }, - "credentialSets": { - "type": "array", - "items": { - "$ref": "#/definitions/credentialSetType" - }, - "nullable": true, - "metadata": { - "description": "Optional. Array of Credential Sets." - } - }, - "scopeMaps": { - "type": "array", - "items": { - "$ref": "#/definitions/scopeMapsType" - }, - "nullable": true, - "metadata": { - "description": "Optional. Scope maps setting." - } - } - }, - "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)))))]" - } - ], - "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", - "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned, UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', 'None')), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", - "builtInRoleNames": { - "AcrDelete": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c2f4ef07-c644-48eb-af81-4b1b4947fb11')]", - "AcrImageSigner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6cef56e8-d556-48e5-a04f-b8e64114680f')]", - "AcrPull": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d')]", - "AcrPush": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8311e382-0749-4cb8-b61a-304f252e45ec')]", - "AcrQuarantineReader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'cdda3590-29a3-44f6-95f2-9f980659eb04')]", - "AcrQuarantineWriter": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c8d4ff99-41c3-41a8-9f60-21dfdad59608')]", - "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" - } + "name": "Key Vaults", + "description": "This module deploys a Key Vault." }, - "resources": { - "cMKKeyVault::cMKKey": { - "condition": "[and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), not(empty(tryGet(parameters('customerManagedKey'), 'keyName')))))]", - "existing": true, - "type": "Microsoft.KeyVault/vaults/keys", - "apiVersion": "2023-02-01", - "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '//'), '/')[2]]", - "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '////'), '/')[4]]", - "name": "[format('{0}/{1}', last(split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), 'dummyVault'), '/')), coalesce(tryGet(parameters('customerManagedKey'), 'keyName'), 'dummyKey'))]" - }, - "avmTelemetry": { - "condition": "[parameters('enableTelemetry')]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2024-03-01", - "name": "[format('46d3xbcp.res.containerregistry-registry.{0}.{1}', replace('0.8.4', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "definitions": { + "privateEndpointOutputType": { + "type": "object", "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" - } + "name": { + "type": "string", + "metadata": { + "description": "The name of the private endpoint." } - } - } - }, - "cMKKeyVault": { - "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId')))]", - "existing": true, - "type": "Microsoft.KeyVault/vaults", - "apiVersion": "2023-02-01", - "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '//'), '/')[2]]", - "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '////'), '/')[4]]", - "name": "[last(split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), 'dummyVault'), '/'))]" - }, - "cMKUserAssignedIdentity": { - "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId')))]", - "existing": true, - "type": "Microsoft.ManagedIdentity/userAssignedIdentities", - "apiVersion": "2023-01-31", - "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '//'), '/')[2]]", - "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '////'), '/')[4]]", - "name": "[last(split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), 'dummyMsi'), '/'))]" - }, - "registry": { - "type": "Microsoft.ContainerRegistry/registries", - "apiVersion": "2023-06-01-preview", - "name": "[parameters('name')]", - "location": "[parameters('location')]", - "identity": "[variables('identity')]", - "tags": "[parameters('tags')]", - "sku": { - "name": "[parameters('acrSku')]" - }, - "properties": { - "anonymousPullEnabled": "[parameters('anonymousPullEnabled')]", - "adminUserEnabled": "[parameters('acrAdminUserEnabled')]", - "encryption": "[if(not(empty(parameters('customerManagedKey'))), createObject('status', 'enabled', 'keyVaultProperties', createObject('identity', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), ''))), reference('cMKUserAssignedIdentity').clientId, null()), 'keyIdentifier', if(not(empty(tryGet(parameters('customerManagedKey'), 'keyVersion'))), format('{0}/{1}', reference('cMKKeyVault::cMKKey').keyUri, tryGet(parameters('customerManagedKey'), 'keyVersion')), if(coalesce(tryGet(parameters('customerManagedKey'), 'autoRotationEnabled'), true()), reference('cMKKeyVault::cMKKey').keyUri, reference('cMKKeyVault::cMKKey').keyUriWithVersion)))), null())]", - "policies": { - "azureADAuthenticationAsArmPolicy": { - "status": "[parameters('azureADAuthenticationAsArmPolicyStatus')]" - }, - "exportPolicy": "[if(equals(parameters('acrSku'), 'Premium'), createObject('status', parameters('exportPolicyStatus')), null())]", - "quarantinePolicy": "[if(equals(parameters('acrSku'), 'Premium'), createObject('status', parameters('quarantinePolicyStatus')), null())]", - "trustPolicy": "[if(equals(parameters('acrSku'), 'Premium'), createObject('type', 'Notary', 'status', parameters('trustPolicyStatus')), null())]", - "retentionPolicy": "[if(equals(parameters('acrSku'), 'Premium'), createObject('days', parameters('retentionPolicyDays'), 'status', parameters('retentionPolicyStatus')), null())]", - "softDeletePolicy": { - "retentionDays": "[parameters('softDeletePolicyDays')]", - "status": "[parameters('softDeletePolicyStatus')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the private endpoint." } }, - "dataEndpointEnabled": "[parameters('dataEndpointEnabled')]", - "publicNetworkAccess": "[if(not(empty(parameters('publicNetworkAccess'))), parameters('publicNetworkAccess'), if(and(not(empty(parameters('privateEndpoints'))), empty(parameters('networkRuleSetIpRules'))), 'Disabled', null()))]", - "networkRuleBypassOptions": "[parameters('networkRuleBypassOptions')]", - "networkRuleSet": "[if(not(empty(parameters('networkRuleSetIpRules'))), createObject('defaultAction', parameters('networkRuleSetDefaultAction'), 'ipRules', parameters('networkRuleSetIpRules')), null())]", - "zoneRedundancy": "[if(equals(parameters('acrSku'), 'Premium'), parameters('zoneRedundancy'), null())]" - }, - "dependsOn": [ - "cMKKeyVault::cMKKey", - "cMKUserAssignedIdentity" - ] - }, - "registry_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.ContainerRegistry/registries/{0}', parameters('name'))]", - "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", - "properties": { - "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", - "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" - }, - "dependsOn": [ - "registry" - ] - }, - "registry_diagnosticSettings": { - "copy": { - "name": "registry_diagnosticSettings", - "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" - }, - "type": "Microsoft.Insights/diagnosticSettings", - "apiVersion": "2021-05-01-preview", - "scope": "[format('Microsoft.ContainerRegistry/registries/{0}', parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", - "properties": { - "copy": [ - { - "name": "metrics", - "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", - "input": { - "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", - "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", - "timeGrain": null + "groupId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "The group Id for the private endpoint Group." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "FQDN that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "A list of private IP addresses of the private endpoint." + } + } } }, - { - "name": "logs", - "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", - "input": { - "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", - "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", - "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" - } + "metadata": { + "description": "The custom DNS configurations of the private endpoint." } - ], - "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", - "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", - "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", - "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", - "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", - "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "networkInterfaceResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "The IDs of the network interfaces associated with the private endpoint." + } + } }, - "dependsOn": [ - "registry" - ] + "metadata": { + "__bicep_export!": true + } }, - "registry_roleAssignments": { - "copy": { - "name": "registry_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.ContainerRegistry/registries/{0}', parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.ContainerRegistry/registries', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "credentialOutputType": { + "type": "object", "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')]" + "resourceId": { + "type": "string", + "metadata": { + "description": "The item's resourceId." + } + }, + "uri": { + "type": "string", + "metadata": { + "description": "The item's uri." + } + }, + "uriWithVersion": { + "type": "string", + "metadata": { + "description": "The item's uri with version." + } + } }, - "dependsOn": [ - "registry" - ] + "metadata": { + "__bicep_export!": true, + "description": "The type for a credential output." + } }, - "registry_scopeMaps": { - "copy": { - "name": "registry_scopeMaps", - "count": "[length(coalesce(parameters('scopeMaps'), createArray()))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-Registry-Scope-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "accessPolicyType": { + "type": "object", "properties": { - "expressionEvaluationOptions": { - "scope": "inner" + "tenantId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The tenant ID that is used for authenticating requests to the key vault." + } }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[tryGet(coalesce(parameters('scopeMaps'), createArray())[copyIndex()], 'name')]" - }, - "actions": { - "value": "[coalesce(parameters('scopeMaps'), createArray())[copyIndex()].actions]" - }, - "description": { - "value": "[tryGet(coalesce(parameters('scopeMaps'), createArray())[copyIndex()], 'description')]" - }, - "registryName": { - "value": "[parameters('name')]" + "objectId": { + "type": "string", + "metadata": { + "description": "Required. The object ID of a user, service principal or security group in the tenant for the vault." } }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "languageVersion": "2.0", - "contentVersion": "1.0.0.0", + "applicationId": { + "type": "string", + "nullable": true, "metadata": { - "_generator": { - "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "11112300500664950599" - }, - "name": "Container Registries scopeMaps", - "description": "This module deploys an Azure Container Registry (ACR) scopeMap." - }, - "parameters": { - "registryName": { - "type": "string", + "description": "Optional. Application ID of the client making request on behalf of a principal." + } + }, + "permissions": { + "type": "object", + "properties": { + "keys": { + "type": "array", + "allowedValues": [ + "all", + "backup", + "create", + "decrypt", + "delete", + "encrypt", + "get", + "getrotationpolicy", + "import", + "list", + "purge", + "recover", + "release", + "restore", + "rotate", + "setrotationpolicy", + "sign", + "unwrapKey", + "update", + "verify", + "wrapKey" + ], + "nullable": true, "metadata": { - "description": "Conditional. The name of the parent registry. Required if the template is used in a standalone deployment." + "description": "Optional. Permissions to keys." } }, - "name": { - "type": "string", - "defaultValue": "[format('{0}-scopemaps', parameters('registryName'))]", + "secrets": { + "type": "array", + "allowedValues": [ + "all", + "backup", + "delete", + "get", + "list", + "purge", + "recover", + "restore", + "set" + ], + "nullable": true, "metadata": { - "description": "Optional. The name of the scope map." + "description": "Optional. Permissions to secrets." } }, - "actions": { + "certificates": { "type": "array", - "items": { - "type": "string" - }, + "allowedValues": [ + "all", + "backup", + "create", + "delete", + "deleteissuers", + "get", + "getissuers", + "import", + "list", + "listissuers", + "managecontacts", + "manageissuers", + "purge", + "recover", + "restore", + "setissuers", + "update" + ], + "nullable": true, "metadata": { - "description": "Required. The list of scoped permissions for registry artifacts." + "description": "Optional. Permissions to certificates." } }, - "description": { - "type": "string", + "storage": { + "type": "array", + "allowedValues": [ + "all", + "backup", + "delete", + "deletesas", + "get", + "getsas", + "list", + "listsas", + "purge", + "recover", + "regeneratekey", + "restore", + "set", + "setsas", + "update" + ], "nullable": true, "metadata": { - "description": "Optional. The user friendly description of the scope map." - } - } - }, - "resources": { - "registry": { - "existing": true, - "type": "Microsoft.ContainerRegistry/registries", - "apiVersion": "2023-06-01-preview", - "name": "[parameters('registryName')]" - }, - "scopeMap": { - "type": "Microsoft.ContainerRegistry/registries/scopeMaps", - "apiVersion": "2023-06-01-preview", - "name": "[format('{0}/{1}', parameters('registryName'), parameters('name'))]", - "properties": { - "actions": "[parameters('actions')]", - "description": "[parameters('description')]" + "description": "Optional. Permissions to storage accounts." } } }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the scope map." - }, - "value": "[parameters('name')]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The name of the resource group the scope map was created in." - }, - "value": "[resourceGroup().name]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the scope map." - }, - "value": "[resourceId('Microsoft.ContainerRegistry/registries/scopeMaps', parameters('registryName'), parameters('name'))]" - } + "metadata": { + "description": "Required. Permissions the identity has for keys, secrets and certificates." } } }, - "dependsOn": [ - "registry" - ] + "metadata": { + "__bicep_export!": true, + "description": "The type for an access policy." + } }, - "registry_replications": { - "copy": { - "name": "registry_replications", - "count": "[length(coalesce(parameters('replications'), createArray()))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-Registry-Replication-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "secretType": { + "type": "object", "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[coalesce(parameters('replications'), createArray())[copyIndex()].name]" - }, - "registryName": { - "value": "[parameters('name')]" - }, - "location": { - "value": "[coalesce(parameters('replications'), createArray())[copyIndex()].location]" - }, - "regionEndpointEnabled": { - "value": "[tryGet(coalesce(parameters('replications'), createArray())[copyIndex()], 'regionEndpointEnabled')]" - }, - "zoneRedundancy": { - "value": "[tryGet(coalesce(parameters('replications'), createArray())[copyIndex()], 'zoneRedundancy')]" - }, - "tags": { - "value": "[coalesce(tryGet(coalesce(parameters('replications'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the secret." } }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "languageVersion": "2.0", - "contentVersion": "1.0.0.0", + "tags": { + "type": "object", + "nullable": true, "metadata": { - "_generator": { - "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "6036875058945996178" - }, - "name": "Azure Container Registry (ACR) Replications", - "description": "This module deploys an Azure Container Registry (ACR) Replication." - }, - "parameters": { - "registryName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent registry. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the replication." - } - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]", - "metadata": { - "description": "Optional. Location for all resources." - } - }, - "tags": { - "type": "object", + "description": "Optional. Resource tags." + } + }, + "attributes": { + "type": "object", + "properties": { + "enabled": { + "type": "bool", "nullable": true, "metadata": { - "description": "Optional. Tags of the resource." + "description": "Optional. Defines whether the secret is enabled or disabled." } }, - "regionEndpointEnabled": { - "type": "bool", - "defaultValue": true, + "exp": { + "type": "int", + "nullable": true, "metadata": { - "description": "Optional. Specifies whether the replication regional endpoint is enabled. Requests will not be routed to a replication whose regional endpoint is disabled, however its data will continue to be synced with other replications." + "description": "Optional. Defines when the secret will become invalid. Defined in seconds since 1970-01-01T00:00:00Z." } }, - "zoneRedundancy": { - "type": "string", - "defaultValue": "Disabled", - "allowedValues": [ - "Disabled", - "Enabled" - ], + "nbf": { + "type": "int", + "nullable": true, "metadata": { - "description": "Optional. Whether or not zone redundancy is enabled for this container registry." + "description": "Optional. If set, defines the date from which onwards the secret becomes valid. Defined in seconds since 1970-01-01T00:00:00Z." } } }, - "resources": { - "registry": { - "existing": true, - "type": "Microsoft.ContainerRegistry/registries", - "apiVersion": "2023-06-01-preview", - "name": "[parameters('registryName')]" - }, - "replication": { - "type": "Microsoft.ContainerRegistry/registries/replications", - "apiVersion": "2023-06-01-preview", - "name": "[format('{0}/{1}', parameters('registryName'), parameters('name'))]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", - "properties": { - "regionEndpointEnabled": "[parameters('regionEndpointEnabled')]", - "zoneRedundancy": "[parameters('zoneRedundancy')]" - } - } + "nullable": true, + "metadata": { + "description": "Optional. Contains attributes of the secret." + } + }, + "contentType": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The content type of the secret." + } + }, + "value": { + "type": "securestring", + "metadata": { + "description": "Required. The value of the secret. NOTE: \"value\" will never be returned from the service, as APIs using this model are is intended for internal use in ARM deployments. Users should use the data-plane REST service for interaction with vault secrets." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the replication." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the replication." - }, - "value": "[resourceId('Microsoft.ContainerRegistry/registries/replications', parameters('registryName'), parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The name of the resource group the replication was created in." - }, - "value": "[resourceGroup().name]" - }, - "location": { - "type": "string", - "metadata": { - "description": "The location the resource was deployed into." - }, - "value": "[reference('replication', '2023-06-01-preview', 'full').location]" - } + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." } } }, - "dependsOn": [ - "registry" - ] + "metadata": { + "__bicep_export!": true, + "description": "The type for a secret output." + } }, - "registry_credentialSets": { - "copy": { - "name": "registry_credentialSets", - "count": "[length(coalesce(parameters('credentialSets'), createArray()))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-Registry-CredentialSet-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "keyType": { + "type": "object", "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[coalesce(parameters('credentialSets'), createArray())[copyIndex()].name]" - }, - "registryName": { - "value": "[parameters('name')]" - }, - "managedIdentities": { - "value": "[coalesce(parameters('credentialSets'), createArray())[copyIndex()].managedIdentities]" - }, - "authCredentials": { - "value": "[coalesce(parameters('credentialSets'), createArray())[copyIndex()].authCredentials]" - }, - "loginServer": { - "value": "[coalesce(parameters('credentialSets'), createArray())[copyIndex()].loginServer]" + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the key." } }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "languageVersion": "2.0", - "contentVersion": "1.0.0.0", + "tags": { + "type": "object", + "nullable": true, "metadata": { - "_generator": { - "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "15848218260506856293" + "description": "Optional. Resource tags." + } + }, + "attributes": { + "type": "object", + "properties": { + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Defines whether the key is enabled or disabled." + } }, - "name": "Container Registries Credential Sets", - "description": "This module deploys an ACR Credential Set." - }, - "definitions": { - "authCredentialsType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the credential." - } - }, - "usernameSecretIdentifier": { - "type": "string", - "metadata": { - "description": "Required. KeyVault Secret URI for accessing the username." - } - }, - "passwordSecretIdentifier": { - "type": "string", - "metadata": { - "description": "Required. KeyVault Secret URI for accessing the password." - } - } - }, + "exp": { + "type": "int", + "nullable": true, "metadata": { - "__bicep_export!": true, - "description": "The type for auth credentials." + "description": "Optional. Defines when the key will become invalid. Defined in seconds since 1970-01-01T00:00:00Z." } }, - "managedIdentityOnlySysAssignedType": { - "type": "object", - "properties": { - "systemAssigned": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Enables system assigned managed identity on the resource." - } - } - }, + "nbf": { + "type": "int", + "nullable": true, "metadata": { - "description": "An AVM-aligned type for a managed identity configuration. To be used if only system-assigned identities are supported by the resource provider.", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } + "description": "Optional. If set, defines the date from which onwards the key becomes valid. Defined in seconds since 1970-01-01T00:00:00Z." } } }, - "parameters": { - "registryName": { + "nullable": true, + "metadata": { + "description": "Optional. Contains attributes of the key." + } + }, + "curveName": { + "type": "string", + "allowedValues": [ + "P-256", + "P-256K", + "P-384", + "P-521" + ], + "nullable": true, + "metadata": { + "description": "Optional. The elliptic curve name. Only works if \"keySize\" equals \"EC\" or \"EC-HSM\". Default is \"P-256\"." + } + }, + "keyOps": { + "type": "array", + "allowedValues": [ + "decrypt", + "encrypt", + "import", + "release", + "sign", + "unwrapKey", + "verify", + "wrapKey" + ], + "nullable": true, + "metadata": { + "description": "Optional. The allowed operations on this key." + } + }, + "keySize": { + "type": "int", + "allowedValues": [ + 2048, + 3072, + 4096 + ], + "nullable": true, + "metadata": { + "description": "Optional. The key size in bits. Only works if \"keySize\" equals \"RSA\" or \"RSA-HSM\". Default is \"4096\"." + } + }, + "kty": { + "type": "string", + "allowedValues": [ + "EC", + "EC-HSM", + "RSA", + "RSA-HSM" + ], + "nullable": true, + "metadata": { + "description": "Optional. The type of the key. Default is \"EC\"." + } + }, + "releasePolicy": { + "type": "object", + "properties": { + "contentType": { "type": "string", + "nullable": true, "metadata": { - "description": "Conditional. The name of the parent registry. Required if the template is used in a standalone deployment." + "description": "Optional. Content type and version of key release policy." } }, - "name": { + "data": { "type": "string", - "metadata": { - "description": "Required. The name of the credential set." - } - }, - "managedIdentities": { - "$ref": "#/definitions/managedIdentityOnlySysAssignedType", "nullable": true, "metadata": { - "description": "Optional. The managed identity definition for this resource." - } - }, - "authCredentials": { - "type": "array", - "items": { - "$ref": "#/definitions/authCredentialsType" - }, - "metadata": { - "description": "Required. List of authentication credentials stored for an upstream. Usually consists of a primary and an optional secondary credential." - } - }, - "loginServer": { - "type": "string", - "metadata": { - "description": "Required. The credentials are stored for this upstream or login server." + "description": "Optional. Blob encoding the policy rules under which the key can be released." } } }, - "variables": { - "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), 'SystemAssigned', null())), null())]" - }, - "resources": { - "registry": { - "existing": true, - "type": "Microsoft.ContainerRegistry/registries", - "apiVersion": "2023-06-01-preview", - "name": "[parameters('registryName')]" - }, - "credentialSet": { - "type": "Microsoft.ContainerRegistry/registries/credentialSets", - "apiVersion": "2023-11-01-preview", - "name": "[format('{0}/{1}', parameters('registryName'), parameters('name'))]", - "identity": "[variables('identity')]", - "properties": { - "authCredentials": "[parameters('authCredentials')]", - "loginServer": "[parameters('loginServer')]" - } - } + "nullable": true, + "metadata": { + "description": "Optional. Key release policy." + } + }, + "rotationPolicy": { + "$ref": "#/definitions/rotationPolicyType", + "nullable": true, + "metadata": { + "description": "Optional. Key rotation policy." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The Name of the Credential Set." - }, - "value": "[parameters('name')]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The name of the Credential Set." - }, - "value": "[resourceGroup().name]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the Credential Set." - }, - "value": "[resourceId('Microsoft.ContainerRegistry/registries/credentialSets', parameters('registryName'), parameters('name'))]" - }, - "systemAssignedMIPrincipalId": { + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for a key." + } + }, + "rotationPolicyType": { + "type": "object", + "properties": { + "attributes": { + "type": "object", + "properties": { + "expiryTime": { "type": "string", "nullable": true, "metadata": { - "description": "The principal ID of the system assigned identity." + "description": "Optional. The expiration time for the new key version. It should be in ISO8601 format. Eg: \"P90D\", \"P1Y\"." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The attributes of key rotation policy." + } + }, + "lifetimeActions": { + "type": "array", + "items": { + "type": "object", + "properties": { + "action": { + "type": "object", + "properties": { + "type": { + "type": "string", + "allowedValues": [ + "Notify", + "Rotate" + ], + "nullable": true, + "metadata": { + "description": "Optional. The type of action." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The action of key rotation policy lifetimeAction." + } }, - "value": "[tryGet(tryGet(reference('credentialSet', '2023-11-01-preview', 'full'), 'identity'), 'principalId')]" + "trigger": { + "type": "object", + "properties": { + "timeAfterCreate": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The time duration after key creation to rotate the key. It only applies to rotate. It will be in ISO 8601 duration format. Eg: \"P90D\", \"P1Y\"." + } + }, + "timeBeforeExpiry": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The time duration before key expiring to rotate or notify. It will be in ISO 8601 duration format. Eg: \"P90D\", \"P1Y\"." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The trigger of key rotation policy lifetimeAction." + } + } } + }, + "nullable": true, + "metadata": { + "description": "Optional. The lifetimeActions for key rotation action." } } }, - "dependsOn": [ - "registry" - ] + "metadata": { + "description": "The type for a rotation policy." + } }, - "registry_cacheRules": { - "copy": { - "name": "registry_cacheRules", - "count": "[length(coalesce(parameters('cacheRules'), createArray()))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-Registry-Cache-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "_1.privateEndpointCustomDnsConfigType": { + "type": "object", "properties": { - "expressionEvaluationOptions": { - "scope": "inner" + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. FQDN that resolves to private endpoint IP address." + } }, - "mode": "Incremental", - "parameters": { - "registryName": { - "value": "[parameters('name')]" - }, - "sourceRepository": { - "value": "[coalesce(parameters('cacheRules'), createArray())[copyIndex()].sourceRepository]" - }, - "name": { - "value": "[tryGet(coalesce(parameters('cacheRules'), createArray())[copyIndex()], 'name')]" - }, - "targetRepository": { - "value": "[coalesce(tryGet(coalesce(parameters('cacheRules'), createArray())[copyIndex()], 'targetRepository'), coalesce(parameters('cacheRules'), createArray())[copyIndex()].sourceRepository)]" + "ipAddresses": { + "type": "array", + "items": { + "type": "string" }, - "credentialSetResourceId": { - "value": "[tryGet(coalesce(parameters('cacheRules'), createArray())[copyIndex()], 'credentialSetResourceId')]" + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "languageVersion": "2.0", - "contentVersion": "1.0.0.0", + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "_1.privateEndpointIpConfigurationType": { + "type": "object", + "properties": { + "name": { + "type": "string", "metadata": { - "_generator": { - "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "3783697279882479947" - }, - "name": "Container Registries Cache", - "description": "Cache for Azure Container Registry (Preview) feature allows users to cache container images in a private container registry. Cache for ACR, is a preview feature available in Basic, Standard, and Premium service tiers ([ref](https://learn.microsoft.com/en-us/azure/container-registry/tutorial-registry-cache))." - }, - "parameters": { - "registryName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent registry. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", - "defaultValue": "[replace(replace(replace(parameters('sourceRepository'), '/', '-'), '.', '-'), '*', '')]", - "metadata": { - "description": "Optional. The name of the cache rule. Will be derived from the source repository name if not defined." - } - }, - "sourceRepository": { + "description": "Required. The name of the resource that is unique within a resource group." + } + }, + "properties": { + "type": "object", + "properties": { + "groupId": { "type": "string", "metadata": { - "description": "Required. Source repository pulled from upstream." + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." } }, - "targetRepository": { + "memberName": { "type": "string", - "defaultValue": "[parameters('sourceRepository')]", "metadata": { - "description": "Optional. Target repository specified in docker pull command. E.g.: docker pull myregistry.azurecr.io/{targetRepository}:{tag}." + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to." } }, - "credentialSetResourceId": { + "privateIPAddress": { "type": "string", - "nullable": true, "metadata": { - "description": "Optional. The resource ID of the credential store which is associated with the cache rule." + "description": "Required. A private IP address obtained from the private endpoint's subnet." } } }, - "resources": { - "registry": { - "existing": true, - "type": "Microsoft.ContainerRegistry/registries", - "apiVersion": "2023-06-01-preview", - "name": "[parameters('registryName')]" - }, - "cacheRule": { - "type": "Microsoft.ContainerRegistry/registries/cacheRules", - "apiVersion": "2023-06-01-preview", - "name": "[format('{0}/{1}', parameters('registryName'), parameters('name'))]", - "properties": { - "sourceRepository": "[parameters('sourceRepository')]", - "targetRepository": "[parameters('targetRepository')]", - "credentialSetResourceId": "[parameters('credentialSetResourceId')]" + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "_1.privateEndpointPrivateDnsZoneGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "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." + } } } }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The Name of the Cache Rule." - }, - "value": "[parameters('name')]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The name of the Cache Rule." - }, - "value": "[resourceGroup().name]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the Cache Rule." - }, - "value": "[resourceId('Microsoft.ContainerRegistry/registries/cacheRules', parameters('registryName'), parameters('name'))]" - } + "metadata": { + "description": "Required. The private DNS Zone Groups to associate the Private Endpoint. A DNS Zone Group can support up to 5 DNS zones." } } }, - "dependsOn": [ - "registry", - "registry_credentialSets" - ] + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } }, - "registry_webhooks": { - "copy": { - "name": "registry_webhooks", - "count": "[length(coalesce(parameters('webhooks'), createArray()))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-Registry-Webhook-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "diagnosticSettingFullType": { + "type": "object", "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[coalesce(parameters('webhooks'), createArray())[copyIndex()].name]" - }, - "registryName": { - "value": "[parameters('name')]" - }, - "location": { - "value": "[coalesce(tryGet(coalesce(parameters('webhooks'), createArray())[copyIndex()], 'location'), parameters('location'))]" - }, - "action": { - "value": "[tryGet(coalesce(parameters('webhooks'), createArray())[copyIndex()], 'action')]" - }, - "customHeaders": { - "value": "[tryGet(coalesce(parameters('webhooks'), createArray())[copyIndex()], 'customHeaders')]" - }, - "scope": { - "value": "[tryGet(coalesce(parameters('webhooks'), createArray())[copyIndex()], 'scope')]" - }, - "status": { - "value": "[tryGet(coalesce(parameters('webhooks'), createArray())[copyIndex()], 'status')]" - }, - "serviceUri": { - "value": "[coalesce(parameters('webhooks'), createArray())[copyIndex()].serviceUri]" - }, - "tags": { - "value": "[coalesce(tryGet(coalesce(parameters('webhooks'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the diagnostic setting." } }, - "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.33.93.31351", - "templateHash": "10084997815751263562" - }, - "name": "Azure Container Registry (ACR) Webhooks", - "description": "This module deploys an Azure Container Registry (ACR) Webhook." - }, - "parameters": { - "registryName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent registry. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", - "defaultValue": "[format('{0}webhook', parameters('registryName'))]", - "minLength": 5, - "maxLength": 50, - "metadata": { - "description": "Optional. The name of the registry webhook." - } - }, - "serviceUri": { - "type": "string", - "metadata": { - "description": "Required. The service URI for the webhook to post notifications." - } - }, - "status": { - "type": "string", - "defaultValue": "enabled", - "allowedValues": [ - "disabled", - "enabled" - ], - "metadata": { - "description": "Optional. The status of the webhook at the time the operation was called." - } - }, - "action": { - "type": "array", - "items": { - "type": "string" + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } }, - "defaultValue": [ - "chart_delete", - "chart_push", - "delete", - "push", - "quarantine" - ], - "metadata": { - "description": "Optional. The list of actions that trigger the webhook to post notifications." - } - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]", - "metadata": { - "description": "Optional. Location for all resources." - } - }, - "tags": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Tags of the resource." - } - }, - "customHeaders": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Custom headers that will be added to the webhook notifications." - } - }, - "scope": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The scope of repositories where the event can be triggered. For example, 'foo:*' means events for all tags under repository 'foo'. 'foo:bar' means events for 'foo:bar' only. 'foo' is equivalent to 'foo:latest'. Empty means all events." + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } } } }, - "resources": { - "registry": { - "existing": true, - "type": "Microsoft.ContainerRegistry/registries", - "apiVersion": "2023-06-01-preview", - "name": "[parameters('registryName')]" - }, - "webhook": { - "type": "Microsoft.ContainerRegistry/registries/webhooks", - "apiVersion": "2023-06-01-preview", - "name": "[format('{0}/{1}', parameters('registryName'), parameters('name'))]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", - "properties": { - "actions": "[parameters('action')]", - "customHeaders": "[parameters('customHeaders')]", - "scope": "[parameters('scope')]", - "serviceUri": "[parameters('serviceUri')]", - "status": "[parameters('status')]" + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "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`." + } } } }, - "outputs": { - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the webhook." - }, - "value": "[resourceId('Microsoft.ContainerRegistry/registries/webhooks', parameters('registryName'), parameters('name'))]" - }, - "name": { - "type": "string", - "metadata": { - "description": "The name of the webhook." - }, - "value": "[parameters('name')]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The name of the Azure container registry." - }, - "value": "[resourceGroup().name]" - }, - "actions": { - "type": "array", - "metadata": { - "description": "The actions of the webhook." - }, - "value": "[reference('webhook').actions]" - }, - "status": { - "type": "string", - "metadata": { - "description": "The status of the webhook." - }, - "value": "[reference('webhook').status]" - }, - "provistioningState": { - "type": "string", - "metadata": { - "description": "The provisioning state of the webhook." - }, - "value": "[reference('webhook').provisioningState]" - }, - "location": { - "type": "string", - "metadata": { - "description": "The location the resource was deployed into." - }, - "value": "[reference('webhook', '2023-06-01-preview', 'full').location]" - } + "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." } } }, - "dependsOn": [ - "registry" - ] + "metadata": { + "description": "An AVM-aligned type for a diagnostic setting. To be used if both logs & metrics are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } }, - "registry_privateEndpoints": { - "copy": { - "name": "registry_privateEndpoints", - "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]" + "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." + } + } }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-registry-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "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.5.1" + } + } + }, + "privateEndpointSingleServiceType": { + "type": "object", "properties": { - "expressionEvaluationOptions": { - "scope": "inner" + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private Endpoint." + } }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'name'), format('pep-{0}-{1}-{2}', last(split(resourceId('Microsoft.ContainerRegistry/registries', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'registry'), copyIndex()))]" - }, - "privateLinkServiceConnections": "[if(not(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true())), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.ContainerRegistry/registries', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'registry'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.ContainerRegistry/registries', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'registry')))))), createObject('value', null()))]", - "manualPrivateLinkServiceConnections": "[if(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true()), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.ContainerRegistry/registries', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'registry'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.ContainerRegistry/registries', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'registry')), 'requestMessage', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualConnectionRequestMessage'), 'Manual approval required.'))))), createObject('value', null()))]", - "subnetResourceId": { - "value": "[coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId]" - }, - "enableTelemetry": { - "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'enableTelemetry'), parameters('enableTelemetry'))]" - }, - "location": { - "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'location'), reference(split(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location)]" - }, - "lock": { - "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" - }, - "privateDnsZoneGroup": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroup')]" - }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'roleAssignments')]" - }, - "tags": { - "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" - }, - "customDnsConfigs": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customDnsConfigs')]" + "location": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The location to deploy the Private Endpoint to." + } + }, + "privateLinkServiceConnectionName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private link connection to create." + } + }, + "service": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The subresource to deploy the Private Endpoint for. For example \"vault\" for a Key Vault Private Endpoint." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + } + }, + "resourceGroupResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the Resource Group the Private Endpoint will be created in. If not specified, the Resource Group of the provided Virtual Network Subnet is used." + } + }, + "privateDnsZoneGroup": { + "$ref": "#/definitions/_1.privateEndpointPrivateDnsZoneGroupType", + "nullable": true, + "metadata": { + "description": "Optional. The private DNS Zone Group to configure for the Private Endpoint." + } + }, + "isManualConnection": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. If Manual Private Link Connection is required." + } + }, + "manualConnectionRequestMessage": { + "type": "string", + "nullable": true, + "maxLength": 140, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with the manual connection request." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/_1.privateEndpointCustomDnsConfigType" }, - "ipConfigurations": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'ipConfigurations')]" + "nullable": true, + "metadata": { + "description": "Optional. Custom DNS configurations." + } + }, + "ipConfigurations": { + "type": "array", + "items": { + "$ref": "#/definitions/_1.privateEndpointIpConfigurationType" }, - "applicationSecurityGroupResourceIds": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'applicationSecurityGroupResourceIds')]" + "nullable": true, + "metadata": { + "description": "Optional. A list of IP configurations of the Private Endpoint. This will be used to map to the first-party Service endpoints." + } + }, + "applicationSecurityGroupResourceIds": { + "type": "array", + "items": { + "type": "string" }, - "customNetworkInterfaceName": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customNetworkInterfaceName')]" + "nullable": true, + "metadata": { + "description": "Optional. Application security groups in which the Private Endpoint IP configuration is included." } }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "languageVersion": "2.0", - "contentVersion": "1.0.0.0", + "customNetworkInterfaceName": { + "type": "string", + "nullable": true, "metadata": { - "_generator": { - "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "15954548978129725136" - }, - "name": "Private Endpoints", - "description": "This module deploys a Private Endpoint." + "description": "Optional. The custom name of the network interface attached to the Private Endpoint." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" }, - "definitions": { - "privateDnsZoneGroupType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the Private DNS Zone Group." - } - }, - "privateDnsZoneGroupConfigs": { - "type": "array", - "items": { - "$ref": "#/definitions/privateDnsZoneGroupConfigType" - }, - "metadata": { - "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." - } - } - }, - "metadata": { - "__bicep_export!": true - } - }, - "ipConfigurationType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the resource that is unique within a resource group." - } - }, - "properties": { - "type": "object", - "properties": { - "groupId": { - "type": "string", - "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." - } - }, - "memberName": { - "type": "string", - "metadata": { - "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." - } - }, - "privateIPAddress": { - "type": "string", - "metadata": { - "description": "Required. A private IP address obtained from the private endpoint's subnet." - } - } - }, - "metadata": { - "description": "Required. Properties of private endpoint IP configurations." - } - } - }, - "metadata": { - "__bicep_export!": true - } - }, - "privateLinkServiceConnectionType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the private link service connection." - } - }, - "properties": { - "type": "object", - "properties": { - "groupIds": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." - } - }, - "privateLinkServiceId": { - "type": "string", - "metadata": { - "description": "Required. The resource id of private link service." - } - }, - "requestMessage": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." - } - } - }, - "metadata": { - "description": "Required. Properties of private link service connection." - } - } - }, - "metadata": { - "__bicep_export!": true - } - }, - "customDnsConfigType": { - "type": "object", - "properties": { - "fqdn": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. FQDN that resolves to private endpoint IP address." - } - }, - "ipAddresses": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. A list of private IP addresses of the private endpoint." - } - } - }, - "metadata": { - "__bicep_export!": true - } - }, - "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." - } - } - }, - "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.5.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": { - "__bicep_imported_from!": { - "sourceTemplate": "private-dns-zone-group/main.bicep" - } - } + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags to be applied on all resources/Resource Groups in this deployment." + } + }, + "enableTelemetry": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a private endpoint. To be used if the private endpoint's default service / groupId can be assumed (i.e., for services that only have one Private Endpoint type like 'vault' for key vault).", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "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.5.1" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "maxLength": 24, + "metadata": { + "description": "Required. Name of the Key Vault. Must be globally unique." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "accessPolicies": { + "type": "array", + "items": { + "$ref": "#/definitions/accessPolicyType" + }, + "nullable": true, + "metadata": { + "description": "Optional. All access policies to create." + } + }, + "secrets": { + "type": "array", + "items": { + "$ref": "#/definitions/secretType" + }, + "nullable": true, + "metadata": { + "description": "Optional. All secrets to create." + } + }, + "keys": { + "type": "array", + "items": { + "$ref": "#/definitions/keyType" + }, + "nullable": true, + "metadata": { + "description": "Optional. All keys to create." + } + }, + "enableVaultForDeployment": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Specifies if the vault is enabled for deployment by script or compute." + } + }, + "enableVaultForTemplateDeployment": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Specifies if the vault is enabled for a template deployment." + } + }, + "enableVaultForDiskEncryption": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Specifies if the azure platform has access to the vault for enabling disk encryption scenarios." + } + }, + "enableSoftDelete": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Switch to enable/disable Key Vault's soft delete feature." + } + }, + "softDeleteRetentionInDays": { + "type": "int", + "defaultValue": 90, + "metadata": { + "description": "Optional. softDelete data retention days. It accepts >=7 and <=90." + } + }, + "enableRbacAuthorization": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Property that controls how data actions are authorized. When true, the key vault will use Role Based Access Control (RBAC) for authorization of data actions, and the access policies specified in vault properties will be ignored. When false, the key vault will use the access policies specified in vault properties, and any policy stored on Azure Resource Manager will be ignored. Note that management actions are always authorized with RBAC." + } + }, + "createMode": { + "type": "string", + "defaultValue": "default", + "metadata": { + "description": "Optional. The vault's create mode to indicate whether the vault need to be recovered or not. - recover or default." + } + }, + "enablePurgeProtection": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Provide 'true' to enable Key Vault's purge protection feature." + } + }, + "sku": { + "type": "string", + "defaultValue": "premium", + "allowedValues": [ + "premium", + "standard" + ], + "metadata": { + "description": "Optional. Specifies the SKU for the vault." + } + }, + "networkAcls": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Rules governing the accessibility of the resource from specific network locations." + } + }, + "publicNetworkAccess": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "", + "Enabled", + "Disabled" + ], + "metadata": { + "description": "Optional. Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set and networkAcls are not set." + } + }, + "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." + } + }, + "privateEndpoints": { + "type": "array", + "items": { + "$ref": "#/definitions/privateEndpointSingleServiceType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Resource tags." + } + }, + "diagnosticSettings": { + "type": "array", + "items": { + "$ref": "#/definitions/diagnosticSettingFullType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "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)))))]" + }, + { + "name": "formattedAccessPolicies", + "count": "[length(coalesce(parameters('accessPolicies'), createArray()))]", + "input": { + "applicationId": "[coalesce(tryGet(coalesce(parameters('accessPolicies'), createArray())[copyIndex('formattedAccessPolicies')], 'applicationId'), '')]", + "objectId": "[coalesce(parameters('accessPolicies'), createArray())[copyIndex('formattedAccessPolicies')].objectId]", + "permissions": "[coalesce(parameters('accessPolicies'), createArray())[copyIndex('formattedAccessPolicies')].permissions]", + "tenantId": "[coalesce(tryGet(coalesce(parameters('accessPolicies'), createArray())[copyIndex('formattedAccessPolicies')], 'tenantId'), tenant().tenantId)]" + } + } + ], + "enableReferencedModulesTelemetry": false, + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Key Vault Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00482a5a-887f-4fb3-b363-3b7fe8e74483')]", + "Key Vault Certificates Officer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4417e6f-fecd-4de8-b567-7b0420556985')]", + "Key Vault Certificate User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'db79e9a7-68ee-4b58-9aeb-b90e7c24fcba')]", + "Key Vault Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f25e0fa2-a7c8-4377-a976-54943a77a395')]", + "Key Vault Crypto Officer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '14b46e9e-c2b7-41b4-b07b-48a6ebf60603')]", + "Key Vault Crypto Service Encryption User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e147488a-f6f5-4113-8e2d-b22465e65bf6')]", + "Key Vault Crypto User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '12338af0-0e69-4776-bea7-57ae8d297424')]", + "Key Vault Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21090545-7ca7-4776-b22c-e363652d74d2')]", + "Key Vault Secrets Officer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b86a8fe4-44ce-4948-aee5-eccb2c155cd7')]", + "Key Vault Secrets User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4633458b-17de-408a-b874-0445c86b69e6')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.keyvault-vault.{0}.{1}', replace('0.12.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" + } + } + } + } + }, + "keyVault": { + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2022-07-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "enabledForDeployment": "[parameters('enableVaultForDeployment')]", + "enabledForTemplateDeployment": "[parameters('enableVaultForTemplateDeployment')]", + "enabledForDiskEncryption": "[parameters('enableVaultForDiskEncryption')]", + "enableSoftDelete": "[parameters('enableSoftDelete')]", + "softDeleteRetentionInDays": "[parameters('softDeleteRetentionInDays')]", + "enableRbacAuthorization": "[parameters('enableRbacAuthorization')]", + "createMode": "[parameters('createMode')]", + "enablePurgeProtection": "[if(parameters('enablePurgeProtection'), parameters('enablePurgeProtection'), null())]", + "tenantId": "[subscription().tenantId]", + "accessPolicies": "[variables('formattedAccessPolicies')]", + "sku": { + "name": "[parameters('sku')]", + "family": "A" + }, + "networkAcls": "[if(not(empty(coalesce(parameters('networkAcls'), createObject()))), createObject('bypass', tryGet(parameters('networkAcls'), 'bypass'), 'defaultAction', tryGet(parameters('networkAcls'), 'defaultAction'), 'virtualNetworkRules', coalesce(tryGet(parameters('networkAcls'), 'virtualNetworkRules'), createArray()), 'ipRules', coalesce(tryGet(parameters('networkAcls'), 'ipRules'), createArray())), null())]", + "publicNetworkAccess": "[if(not(empty(parameters('publicNetworkAccess'))), parameters('publicNetworkAccess'), if(and(not(empty(coalesce(parameters('privateEndpoints'), createArray()))), empty(coalesce(parameters('networkAcls'), createObject()))), 'Disabled', null()))]" + } + }, + "keyVault_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.KeyVault/vaults/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "keyVault" + ] + }, + "keyVault_diagnosticSettings": { + "copy": { + "name": "keyVault_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.KeyVault/vaults/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null + } + }, + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "keyVault" + ] + }, + "keyVault_roleAssignments": { + "copy": { + "name": "keyVault_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.KeyVault/vaults/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.KeyVault/vaults', 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": [ + "keyVault" + ] + }, + "keyVault_accessPolicies": { + "condition": "[not(empty(parameters('accessPolicies')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-KeyVault-AccessPolicies', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "keyVaultName": { + "value": "[parameters('name')]" + }, + "accessPolicies": { + "value": "[parameters('accessPolicies')]" + } + }, + "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.33.93.31351", + "templateHash": "6321524620984159084" }, - "roleAssignmentType": { + "name": "Key Vault Access Policies", + "description": "This module deploys a Key Vault Access Policy." + }, + "definitions": { + "accessPoliciesType": { "type": "object", "properties": { - "name": { + "tenantId": { "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." + "description": "Optional. The tenant ID that is used for authenticating requests to the key vault." } }, - "principalType": { + "objectId": { "type": "string", - "allowedValues": [ - "Device", - "ForeignGroup", - "Group", - "ServicePrincipal", - "User" - ], - "nullable": true, "metadata": { - "description": "Optional. The principal type of the assigned principal ID." + "description": "Required. The object ID of a user, service principal or security group in the tenant for the vault." } }, - "description": { + "applicationId": { "type": "string", "nullable": true, "metadata": { - "description": "Optional. The description of the role assignment." + "description": "Optional. Application ID of the client making request on behalf of a principal." } }, - "condition": { + "permissions": { + "type": "object", + "properties": { + "keys": { + "type": "array", + "allowedValues": [ + "all", + "backup", + "create", + "decrypt", + "delete", + "encrypt", + "get", + "getrotationpolicy", + "import", + "list", + "purge", + "recover", + "release", + "restore", + "rotate", + "setrotationpolicy", + "sign", + "unwrapKey", + "update", + "verify", + "wrapKey" + ], + "nullable": true, + "metadata": { + "description": "Optional. Permissions to keys." + } + }, + "secrets": { + "type": "array", + "allowedValues": [ + "all", + "backup", + "delete", + "get", + "list", + "purge", + "recover", + "restore", + "set" + ], + "nullable": true, + "metadata": { + "description": "Optional. Permissions to secrets." + } + }, + "certificates": { + "type": "array", + "allowedValues": [ + "all", + "backup", + "create", + "delete", + "deleteissuers", + "get", + "getissuers", + "import", + "list", + "listissuers", + "managecontacts", + "manageissuers", + "purge", + "recover", + "restore", + "setissuers", + "update" + ], + "nullable": true, + "metadata": { + "description": "Optional. Permissions to certificates." + } + }, + "storage": { + "type": "array", + "allowedValues": [ + "all", + "backup", + "delete", + "deletesas", + "get", + "getsas", + "list", + "listsas", + "purge", + "recover", + "regeneratekey", + "restore", + "set", + "setsas", + "update" + ], + "nullable": true, + "metadata": { + "description": "Optional. Permissions to storage accounts." + } + } + }, + "metadata": { + "description": "Required. Permissions the identity has for keys, secrets and certificates." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for an access policy." + } + } + }, + "parameters": { + "keyVaultName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent key vault. Required if the template is used in a standalone deployment." + } + }, + "accessPolicies": { + "type": "array", + "items": { + "$ref": "#/definitions/accessPoliciesType" + }, + "nullable": true, + "metadata": { + "description": "Optional. An array of 0 to 16 identities that have access to the key vault. All identities in the array must use the same tenant ID as the key vault's tenant ID." + } + } + }, + "resources": { + "keyVault": { + "existing": true, + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2022-07-01", + "name": "[parameters('keyVaultName')]" + }, + "policies": { + "type": "Microsoft.KeyVault/vaults/accessPolicies", + "apiVersion": "2023-07-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), 'add')]", + "properties": { + "copy": [ + { + "name": "accessPolicies", + "count": "[length(coalesce(parameters('accessPolicies'), createArray()))]", + "input": { + "applicationId": "[coalesce(tryGet(coalesce(parameters('accessPolicies'), createArray())[copyIndex('accessPolicies')], 'applicationId'), '')]", + "objectId": "[coalesce(parameters('accessPolicies'), createArray())[copyIndex('accessPolicies')].objectId]", + "permissions": "[coalesce(parameters('accessPolicies'), createArray())[copyIndex('accessPolicies')].permissions]", + "tenantId": "[coalesce(tryGet(coalesce(parameters('accessPolicies'), createArray())[copyIndex('accessPolicies')], 'tenantId'), tenant().tenantId)]" + } + } + ] + } + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the access policies assignment was created in." + }, + "value": "[resourceGroup().name]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the access policies assignment." + }, + "value": "add" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the access policies assignment." + }, + "value": "[resourceId('Microsoft.KeyVault/vaults/accessPolicies', parameters('keyVaultName'), 'add')]" + } + } + } + }, + "dependsOn": [ + "keyVault" + ] + }, + "keyVault_secrets": { + "copy": { + "name": "keyVault_secrets", + "count": "[length(coalesce(parameters('secrets'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-KeyVault-Secret-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(parameters('secrets'), createArray())[copyIndex()].name]" + }, + "value": { + "value": "[coalesce(parameters('secrets'), createArray())[copyIndex()].value]" + }, + "keyVaultName": { + "value": "[parameters('name')]" + }, + "attributesEnabled": { + "value": "[tryGet(tryGet(coalesce(parameters('secrets'), createArray())[copyIndex()], 'attributes'), 'enabled')]" + }, + "attributesExp": { + "value": "[tryGet(tryGet(coalesce(parameters('secrets'), createArray())[copyIndex()], 'attributes'), 'exp')]" + }, + "attributesNbf": { + "value": "[tryGet(tryGet(coalesce(parameters('secrets'), createArray())[copyIndex()], 'attributes'), 'nbf')]" + }, + "contentType": { + "value": "[tryGet(coalesce(parameters('secrets'), createArray())[copyIndex()], 'contentType')]" + }, + "tags": { + "value": "[coalesce(tryGet(coalesce(parameters('secrets'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('secrets'), createArray())[copyIndex()], 'roleAssignments')]" + } + }, + "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.33.93.31351", + "templateHash": "4741547827723795923" + }, + "name": "Key Vault Secrets", + "description": "This module deploys a Key Vault Secret." + }, + "definitions": { + "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": { @@ -17569,64 +15688,57 @@ } }, "parameters": { - "name": { + "keyVaultName": { "type": "string", "metadata": { - "description": "Required. Name of the private endpoint resource to create." + "description": "Conditional. The name of the parent key vault. Required if the template is used in a standalone deployment." } }, - "subnetResourceId": { + "name": { "type": "string", "metadata": { - "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + "description": "Required. The name of the secret." } }, - "applicationSecurityGroupResourceIds": { - "type": "array", - "items": { - "type": "string" - }, + "tags": { + "type": "object", "nullable": true, "metadata": { - "description": "Optional. Application security groups in which the private endpoint IP configuration is included." + "description": "Optional. Resource tags." } }, - "customNetworkInterfaceName": { - "type": "string", - "nullable": true, + "attributesEnabled": { + "type": "bool", + "defaultValue": true, "metadata": { - "description": "Optional. The custom name of the network interface attached to the private endpoint." + "description": "Optional. Determines whether the object is enabled." } }, - "ipConfigurations": { - "type": "array", - "items": { - "$ref": "#/definitions/ipConfigurationType" - }, + "attributesExp": { + "type": "int", "nullable": true, "metadata": { - "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." + "description": "Optional. Expiry date in seconds since 1970-01-01T00:00:00Z. For security reasons, it is recommended to set an expiration date whenever possible." } }, - "privateDnsZoneGroup": { - "$ref": "#/definitions/privateDnsZoneGroupType", + "attributesNbf": { + "type": "int", "nullable": true, "metadata": { - "description": "Optional. The private DNS zone group to configure for the private endpoint." + "description": "Optional. Not before date in seconds since 1970-01-01T00:00:00Z." } }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]", + "contentType": { + "type": "securestring", + "nullable": true, "metadata": { - "description": "Optional. Location for all Resources." + "description": "Optional. The content type of the secret." } }, - "lock": { - "$ref": "#/definitions/lockType", - "nullable": true, + "value": { + "type": "securestring", "metadata": { - "description": "Optional. The lock settings of the service." + "description": "Required. The value of the secret. NOTE: \"value\" will never be returned from the service, as APIs using this model are is intended for internal use in ARM deployments. Users should use the data-plane REST service for interaction with vault secrets." } }, "roleAssignments": { @@ -17638,50 +15750,6 @@ "metadata": { "description": "Optional. Array of role assignments to create." } - }, - "tags": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." - } - }, - "customDnsConfigs": { - "type": "array", - "items": { - "$ref": "#/definitions/customDnsConfigType" - }, - "nullable": true, - "metadata": { - "description": "Optional. Custom DNS configurations." - } - }, - "manualPrivateLinkServiceConnections": { - "type": "array", - "items": { - "$ref": "#/definitions/privateLinkServiceConnectionType" - }, - "nullable": true, - "metadata": { - "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." - } - }, - "privateLinkServiceConnections": { - "type": "array", - "items": { - "$ref": "#/definitions/privateLinkServiceConnectionType" - }, - "nullable": true, - "metadata": { - "description": "Conditional. A grouping of information about the connection to the remote resource. Required if `manualPrivateLinkServiceConnections` is empty." - } - }, - "enableTelemetry": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Optional. Enable/Disable usage telemetry for module." - } } }, "variables": { @@ -17694,87 +15762,48 @@ ], "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')]", + "Key Vault Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00482a5a-887f-4fb3-b363-3b7fe8e74483')]", + "Key Vault Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f25e0fa2-a7c8-4377-a976-54943a77a395')]", + "Key Vault Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21090545-7ca7-4776-b22c-e363652d74d2')]", + "Key Vault Secrets Officer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b86a8fe4-44ce-4948-aee5-eccb2c155cd7')]", + "Key Vault Secrets User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4633458b-17de-408a-b874-0445c86b69e6')]", "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')]" + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, "resources": { - "avmTelemetry": { - "condition": "[parameters('enableTelemetry')]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2024-03-01", - "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.10.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" - } - } - } - } + "keyVault": { + "existing": true, + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2022-07-01", + "name": "[parameters('keyVaultName')]" }, - "privateEndpoint": { - "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2023-11-01", - "name": "[parameters('name')]", - "location": "[parameters('location')]", + "secret": { + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2022-07-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('name'))]", "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')]" - } + "contentType": "[parameters('contentType')]", + "attributes": { + "enabled": "[parameters('attributesEnabled')]", + "exp": "[parameters('attributesExp')]", + "nbf": "[parameters('attributesNbf')]" + }, + "value": "[parameters('value')]" } }, - "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": "[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": { + "secret_roleAssignments": { "copy": { - "name": "privateEndpoint_roleAssignments", + "name": "secret_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))]", + "scope": "[format('Microsoft.KeyVault/vaults/{0}/secrets/{1}', parameters('keyVaultName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.KeyVault/vaults/secrets', parameters('keyVaultName'), 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]", @@ -17785,1522 +15814,854 @@ "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" }, "dependsOn": [ - "privateEndpoint" + "secret" ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the secret." + }, + "value": "[parameters('name')]" }, - "privateEndpoint_privateDnsZoneGroup": { - "condition": "[not(empty(parameters('privateDnsZoneGroup')))]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-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.33.13.18514", - "templateHash": "5440815542537978381" - }, - "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 - } - } - }, - "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." - } - } - }, - "variables": { - "copy": [ - { - "name": "privateDnsZoneConfigsVar", - "count": "[length(parameters('privateDnsZoneConfigs'))]", - "input": { - "name": "[coalesce(tryGet(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')], 'name'), last(split(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId, '/')))]", - "properties": { - "privateDnsZoneId": "[parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId]" - } - } - } - ] - }, - "resources": { - "privateEndpoint": { - "existing": true, - "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2023-11-01", - "name": "[parameters('privateEndpointName')]" - }, - "privateDnsZoneGroup": { - "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2023-11-01", - "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", - "properties": { - "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" - } - } - }, - "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]" - } - } - } + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the secret." }, - "dependsOn": [ - "privateEndpoint" - ] - } - }, - "outputs": { + "value": "[resourceId('Microsoft.KeyVault/vaults/secrets', parameters('keyVaultName'), parameters('name'))]" + }, + "secretUri": { + "type": "string", + "metadata": { + "description": "The uri of the secret." + }, + "value": "[reference('secret').secretUri]" + }, + "secretUriWithVersion": { + "type": "string", + "metadata": { + "description": "The uri with version of the secret." + }, + "value": "[reference('secret').secretUriWithVersion]" + }, "resourceGroupName": { "type": "string", "metadata": { - "description": "The resource group the private endpoint was deployed into." + "description": "The name of the resource group the secret was created in." }, "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "keyVault" + ] + }, + "keyVault_keys": { + "copy": { + "name": "keyVault_keys", + "count": "[length(coalesce(parameters('keys'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-KeyVault-Key-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(parameters('keys'), createArray())[copyIndex()].name]" + }, + "keyVaultName": { + "value": "[parameters('name')]" + }, + "attributesEnabled": { + "value": "[tryGet(tryGet(coalesce(parameters('keys'), createArray())[copyIndex()], 'attributes'), 'enabled')]" + }, + "attributesExp": { + "value": "[tryGet(tryGet(coalesce(parameters('keys'), createArray())[copyIndex()], 'attributes'), 'exp')]" + }, + "attributesNbf": { + "value": "[tryGet(tryGet(coalesce(parameters('keys'), createArray())[copyIndex()], 'attributes'), 'nbf')]" + }, + "curveName": "[if(and(not(equals(tryGet(coalesce(parameters('keys'), createArray())[copyIndex()], 'kty'), 'RSA')), not(equals(tryGet(coalesce(parameters('keys'), createArray())[copyIndex()], 'kty'), 'RSA-HSM'))), createObject('value', coalesce(tryGet(coalesce(parameters('keys'), createArray())[copyIndex()], 'curveName'), 'P-256')), createObject('value', null()))]", + "keyOps": { + "value": "[tryGet(coalesce(parameters('keys'), createArray())[copyIndex()], 'keyOps')]" + }, + "keySize": "[if(or(equals(tryGet(coalesce(parameters('keys'), createArray())[copyIndex()], 'kty'), 'RSA'), equals(tryGet(coalesce(parameters('keys'), createArray())[copyIndex()], 'kty'), 'RSA-HSM')), createObject('value', coalesce(tryGet(coalesce(parameters('keys'), createArray())[copyIndex()], 'keySize'), 4096)), createObject('value', null()))]", + "releasePolicy": { + "value": "[coalesce(tryGet(coalesce(parameters('keys'), createArray())[copyIndex()], 'releasePolicy'), createObject())]" + }, + "kty": { + "value": "[coalesce(tryGet(coalesce(parameters('keys'), createArray())[copyIndex()], 'kty'), 'EC')]" + }, + "tags": { + "value": "[coalesce(tryGet(coalesce(parameters('keys'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('keys'), createArray())[copyIndex()], 'roleAssignments')]" + }, + "rotationPolicy": { + "value": "[tryGet(coalesce(parameters('keys'), createArray())[copyIndex()], 'rotationPolicy')]" + } + }, + "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.33.93.31351", + "templateHash": "12000970886778046699" }, - "resourceId": { + "name": "Key Vault Keys", + "description": "This module deploys a Key Vault Key." + }, + "definitions": { + "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.5.1" + } + } + } + }, + "parameters": { + "keyVaultName": { "type": "string", "metadata": { - "description": "The resource ID of the private endpoint." - }, - "value": "[resourceId('Microsoft.Network/privateEndpoints', parameters('name'))]" + "description": "Conditional. The name of the parent key vault. Required if the template is used in a standalone deployment." + } }, "name": { "type": "string", "metadata": { - "description": "The name of the private endpoint." - }, - "value": "[parameters('name')]" + "description": "Required. The name of the key." + } }, - "location": { + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Resource tags." + } + }, + "attributesEnabled": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Determines whether the object is enabled." + } + }, + "attributesExp": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Expiry date in seconds since 1970-01-01T00:00:00Z. For security reasons, it is recommended to set an expiration date whenever possible." + } + }, + "attributesNbf": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Not before date in seconds since 1970-01-01T00:00:00Z." + } + }, + "curveName": { + "type": "string", + "defaultValue": "P-256", + "allowedValues": [ + "P-256", + "P-256K", + "P-384", + "P-521" + ], + "metadata": { + "description": "Optional. The elliptic curve name." + } + }, + "keyOps": { + "type": "array", + "nullable": true, + "allowedValues": [ + "decrypt", + "encrypt", + "import", + "sign", + "unwrapKey", + "verify", + "wrapKey" + ], + "metadata": { + "description": "Optional. Array of JsonWebKeyOperation." + } + }, + "keySize": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The key size in bits. For example: 2048, 3072, or 4096 for RSA." + } + }, + "kty": { "type": "string", + "defaultValue": "EC", + "allowedValues": [ + "EC", + "EC-HSM", + "RSA", + "RSA-HSM" + ], "metadata": { - "description": "The location the resource was deployed into." - }, - "value": "[reference('privateEndpoint', '2023-11-01', 'full').location]" + "description": "Optional. The type of the key." + } }, - "customDnsConfigs": { + "releasePolicy": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Key release policy." + } + }, + "roleAssignments": { "type": "array", "items": { - "$ref": "#/definitions/customDnsConfigType" + "$ref": "#/definitions/roleAssignmentType" }, + "nullable": true, "metadata": { - "description": "The custom DNS configurations of the private endpoint." + "description": "Optional. Array of role assignments to create." + } + }, + "rotationPolicy": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Key rotation policy properties object." + } + } + }, + "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')]", + "Key Vault Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00482a5a-887f-4fb3-b363-3b7fe8e74483')]", + "Key Vault Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f25e0fa2-a7c8-4377-a976-54943a77a395')]", + "Key Vault Crypto Officer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '14b46e9e-c2b7-41b4-b07b-48a6ebf60603')]", + "Key Vault Crypto Service Encryption User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e147488a-f6f5-4113-8e2d-b22465e65bf6')]", + "Key Vault Crypto User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '12338af0-0e69-4776-bea7-57ae8d297424')]", + "Key Vault Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21090545-7ca7-4776-b22c-e363652d74d2')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "keyVault": { + "existing": true, + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2022-07-01", + "name": "[parameters('keyVaultName')]" + }, + "key": { + "type": "Microsoft.KeyVault/vaults/keys", + "apiVersion": "2022-07-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('name'))]", + "tags": "[parameters('tags')]", + "properties": "[shallowMerge(createArray(createObject('attributes', createObject('enabled', parameters('attributesEnabled'), 'exp', parameters('attributesExp'), 'nbf', parameters('attributesNbf')), 'curveName', parameters('curveName'), 'keyOps', parameters('keyOps'), 'keySize', parameters('keySize'), 'kty', parameters('kty'), 'release_policy', coalesce(parameters('releasePolicy'), createObject())), if(not(empty(parameters('rotationPolicy'))), createObject('rotationPolicy', parameters('rotationPolicy')), createObject())))]" + }, + "key_roleAssignments": { + "copy": { + "name": "key_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, - "value": "[reference('privateEndpoint').customDnsConfigs]" + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.KeyVault/vaults/{0}/keys/{1}', parameters('keyVaultName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.KeyVault/vaults/keys', parameters('keyVaultName'), 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": [ + "key" + ] + } + }, + "outputs": { + "keyUri": { + "type": "string", + "metadata": { + "description": "The uri of the key." + }, + "value": "[reference('key').keyUri]" }, - "networkInterfaceResourceIds": { - "type": "array", - "items": { - "type": "string" + "keyUriWithVersion": { + "type": "string", + "metadata": { + "description": "The uri with version of the key." }, + "value": "[reference('key').keyUriWithVersion]" + }, + "name": { + "type": "string", "metadata": { - "description": "The resource IDs of the network interfaces associated with the private endpoint." + "description": "The name of the key." }, - "value": "[map(reference('privateEndpoint').networkInterfaces, lambda('nic', lambdaVariables('nic').id))]" + "value": "[parameters('name')]" }, - "groupId": { + "resourceId": { "type": "string", - "nullable": true, "metadata": { - "description": "The group Id for the private endpoint Group." + "description": "The resource ID of the key." }, - "value": "[coalesce(tryGet(tryGet(tryGet(tryGet(reference('privateEndpoint'), 'manualPrivateLinkServiceConnections'), 0, 'properties'), 'groupIds'), 0), tryGet(tryGet(tryGet(tryGet(reference('privateEndpoint'), 'privateLinkServiceConnections'), 0, 'properties'), 'groupIds'), 0))]" + "value": "[resourceId('Microsoft.KeyVault/vaults/keys', parameters('keyVaultName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the key was created in." + }, + "value": "[resourceGroup().name]" } } } }, "dependsOn": [ - "registry", - "registry_replications" + "keyVault" ] - } - }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The Name of the Azure container registry." - }, - "value": "[parameters('name')]" - }, - "loginServer": { - "type": "string", - "metadata": { - "description": "The reference to the Azure container registry." - }, - "value": "[reference(resourceId('Microsoft.ContainerRegistry/registries', parameters('name')), '2019-05-01').loginServer]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The name of the Azure container registry." - }, - "value": "[resourceGroup().name]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the Azure container registry." - }, - "value": "[resourceId('Microsoft.ContainerRegistry/registries', parameters('name'))]" - }, - "systemAssignedMIPrincipalId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "The principal ID of the system assigned identity." - }, - "value": "[tryGet(tryGet(reference('registry', '2023-06-01-preview', 'full'), 'identity'), 'principalId')]" - }, - "location": { - "type": "string", - "metadata": { - "description": "The location the resource was deployed into." - }, - "value": "[reference('registry', '2023-06-01-preview', 'full').location]" - }, - "credentialSetsSystemAssignedMIPrincipalIds": { - "type": "array", - "metadata": { - "description": "The Principal IDs of the ACR Credential Sets system-assigned identities." - }, - "copy": { - "count": "[length(range(0, length(coalesce(parameters('credentialSets'), createArray()))))]", - "input": "[tryGet(tryGet(reference(format('registry_credentialSets[{0}]', range(0, length(coalesce(parameters('credentialSets'), createArray())))[copyIndex()])).outputs, 'systemAssignedMIPrincipalId'), 'value')]" - } }, - "credentialSetsResourceIds": { - "type": "array", - "metadata": { - "description": "The Resource IDs of the ACR Credential Sets." - }, + "keyVault_privateEndpoints": { "copy": { - "count": "[length(range(0, length(coalesce(parameters('credentialSets'), createArray()))))]", - "input": "[reference(format('registry_credentialSets[{0}]', range(0, length(coalesce(parameters('credentialSets'), createArray())))[copyIndex()])).outputs.resourceId.value]" - } - }, - "privateEndpoints": { - "type": "array", - "items": { - "$ref": "#/definitions/privateEndpointOutputType" - }, - "metadata": { - "description": "The private endpoints of the Azure container registry." + "name": "keyVault_privateEndpoints", + "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]" }, - "copy": { - "count": "[length(if(not(empty(parameters('privateEndpoints'))), array(parameters('privateEndpoints')), createArray()))]", - "input": { - "name": "[reference(format('registry_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", - "resourceId": "[reference(format('registry_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", - "groupId": "[tryGet(tryGet(reference(format('registry_privateEndpoints[{0}]', copyIndex())).outputs, 'groupId'), 'value')]", - "customDnsConfigs": "[reference(format('registry_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfigs.value]", - "networkInterfaceResourceIds": "[reference(format('registry_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceResourceIds.value]" - } - } - } - } - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', 'private-dns-acr-deployment')]" - ] - } - ], - "outputs": { - "resourceId": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', take(format('{0}-container-registry-deployment', variables('nameFormatted')), 64)), '2022-09-01').outputs.resourceId.value]" - }, - "loginServer": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', take(format('{0}-container-registry-deployment', variables('nameFormatted')), 64)), '2022-09-01').outputs.loginServer.value]" - }, - "name": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', take(format('{0}-container-registry-deployment', variables('nameFormatted')), 64)), '2022-09-01').outputs.name.value]" - } - } - } - }, - "dependsOn": [ - "logAnalyticsWorkspace", - "network" - ] - }, - "storageAccount": { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[take(format('{0}-storage-account-deployment', parameters('name')), 64)]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "storageName": { - "value": "[format('st{0}{1}', parameters('name'), variables('resourceToken'))]" - }, - "location": { - "value": "[parameters('location')]" - }, - "networkIsolation": { - "value": "[parameters('networkIsolation')]" - }, - "virtualNetworkResourceId": "[if(parameters('networkIsolation'), createObject('value', reference('network').outputs.virtualNetworkId.value), createObject('value', ''))]", - "virtualNetworkSubnetResourceId": "[if(parameters('networkIsolation'), createObject('value', reference('network').outputs.vmSubnetId.value), createObject('value', ''))]", - "logAnalyticsWorkspaceResourceId": { - "value": "[reference('logAnalyticsWorkspace').outputs.resourceId.value]" - }, - "roleAssignments": { - "value": "[concat(if(empty(parameters('userObjectId')), createArray(), createArray(createObject('principalId', parameters('userObjectId'), 'principalType', 'User', 'roleDefinitionIdOrName', 'Storage Blob Data Contributor'))), createArray(createObject('principalId', reference('cognitiveServices').outputs.aiServicesSystemAssignedMIPrincipalId.value, 'principalType', 'ServicePrincipal', 'roleDefinitionIdOrName', 'Storage Blob Data Contributor')), if(parameters('searchEnabled'), createArray(createObject('principalId', if(parameters('searchEnabled'), reference('aiSearch').outputs.systemAssignedMIPrincipalId.value, ''), 'principalType', 'ServicePrincipal', 'roleDefinitionIdOrName', 'Storage Blob Data Contributor')), createArray()))]" - }, - "tags": { - "value": "[variables('allTags')]" - } - }, - "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.1.42791", - "templateHash": "8737464205872383016" - } - }, - "definitions": { - "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.5.1" - } - } - } - }, - "parameters": { - "storageName": { - "type": "string", - "metadata": { - "description": "Name of the Storage Account." - } - }, - "location": { - "type": "string", - "metadata": { - "description": "Specifies the location for all the Azure resources." - } - }, - "tags": { - "type": "object", - "defaultValue": {}, - "metadata": { - "description": "Optional. Tags to be applied to the resources." - } - }, - "virtualNetworkResourceId": { - "type": "string", - "metadata": { - "description": "Resource ID of the virtual network to link the private DNS zones." - } - }, - "virtualNetworkSubnetResourceId": { - "type": "string", - "metadata": { - "description": "Resource ID of the subnet for the private endpoint." - } - }, - "logAnalyticsWorkspaceResourceId": { - "type": "string", - "metadata": { - "description": "Resource ID of the Log Analytics workspace to use for diagnostic settings." - } - }, - "networkIsolation": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Specifies whether network isolation is enabled. This will create a private endpoint for the Storage Account and link the private DNS zone." - } - }, - "roleAssignments": { - "type": "array", - "items": { - "$ref": "#/definitions/roleAssignmentType" - }, - "nullable": true, - "metadata": { - "description": "Optional. Array of role assignments to create." - } - } - }, - "variables": { - "nameFormatted": "[take(toLower(parameters('storageName')), 24)]" - }, - "resources": { - "blobPrivateDnsZone": { - "condition": "[parameters('networkIsolation')]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "private-dns-blob-deployment", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[format('privatelink.blob.{0}', environment().suffixes.storage)]" - }, - "virtualNetworkLinks": { - "value": [ - { - "virtualNetworkResourceId": "[parameters('virtualNetworkResourceId')]" - } - ] - }, - "tags": { - "value": "[parameters('tags')]" - } - }, - "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.32.4.45862", - "templateHash": "83178825086050429" - }, - "name": "Private DNS Zones", - "description": "This module deploys a Private DNS zone.", - "owner": "Azure/module-maintainers" - }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "type": "object", - "properties": { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-keyVault-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]]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." - } + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'name'), format('pep-{0}-{1}-{2}', last(split(resourceId('Microsoft.KeyVault/vaults', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'vault'), copyIndex()))]" }, - "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'." - } + "privateLinkServiceConnections": "[if(not(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true())), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.KeyVault/vaults', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'vault'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.KeyVault/vaults', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'vault')))))), createObject('value', null()))]", + "manualPrivateLinkServiceConnections": "[if(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true()), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.KeyVault/vaults', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'vault'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.KeyVault/vaults', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'vault')), 'requestMessage', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualConnectionRequestMessage'), 'Manual approval required.'))))), createObject('value', null()))]", + "subnetResourceId": { + "value": "[coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId]" }, - "principalId": { - "type": "string", - "metadata": { - "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." - } + "enableTelemetry": { + "value": "[variables('enableReferencedModulesTelemetry')]" }, - "principalType": { - "type": "string", - "allowedValues": [ - "Device", - "ForeignGroup", - "Group", - "ServicePrincipal", - "User" - ], - "nullable": true, - "metadata": { - "description": "Optional. The principal type of the assigned principal ID." - } + "location": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'location'), reference(split(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location)]" }, - "description": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The description of the role assignment." - } + "lock": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" }, - "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\"." - } + "privateDnsZoneGroup": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroup')]" }, - "conditionVersion": { - "type": "string", - "allowedValues": [ - "2.0" - ], - "nullable": true, - "metadata": { - "description": "Optional. Version of the condition." - } + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'roleAssignments')]" }, - "delegatedManagedIdentityResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The Resource Id of the delegated managed identity resource." - } - } - } - }, - "nullable": true - }, - "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." - } - } - }, - "nullable": true - }, - "aType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } + "tags": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } + "customDnsConfigs": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customDnsConfigs')]" }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } + "ipConfigurations": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'ipConfigurations')]" }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } + "applicationSecurityGroupResourceIds": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'applicationSecurityGroupResourceIds')]" }, - "aRecords": { - "type": "array", - "items": { + "customNetworkInterfaceName": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customNetworkInterfaceName')]" + } + }, + "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.33.13.18514", + "templateHash": "15954548978129725136" + }, + "name": "Private Endpoints", + "description": "This module deploys a Private Endpoint." + }, + "definitions": { + "privateDnsZoneGroupType": { "type": "object", "properties": { - "ipv4Address": { + "name": { "type": "string", + "nullable": true, "metadata": { - "description": "Required. The IPv4 address of this A record." + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, + "metadata": { + "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." } } + }, + "metadata": { + "__bicep_export!": true } }, - "nullable": true, - "metadata": { - "description": "Optional. The list of A records in the record set." - } - } - } - }, - "nullable": true - }, - "aaaaType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } - }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "aaaaRecords": { - "type": "array", - "items": { + "ipConfigurationType": { "type": "object", "properties": { - "ipv6Address": { + "name": { "type": "string", "metadata": { - "description": "Required. The IPv6 address of this AAAA record." + "description": "Required. The name of the resource that is unique within a resource group." + } + }, + "properties": { + "type": "object", + "properties": { + "groupId": { + "type": "string", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } + } + }, + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." } } + }, + "metadata": { + "__bicep_export!": true } }, - "nullable": true, - "metadata": { - "description": "Optional. The list of AAAA records in the record set." - } - } - } - }, - "nullable": true - }, - "cnameType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } - }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "cnameRecord": { - "type": "object", - "properties": { - "cname": { - "type": "string", - "metadata": { - "description": "Required. The canonical name of the CNAME record." - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. The CNAME record in the record set." - } - } - } - }, - "nullable": true - }, - "mxType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } - }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "mxRecords": { - "type": "array", - "items": { + "privateLinkServiceConnectionType": { "type": "object", "properties": { - "exchange": { + "name": { "type": "string", "metadata": { - "description": "Required. The domain name of the mail host for this MX record." + "description": "Required. The name of the private link service connection." } }, - "preference": { - "type": "int", + "properties": { + "type": "object", + "properties": { + "groupIds": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." + } + }, + "privateLinkServiceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of private link service." + } + }, + "requestMessage": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." + } + } + }, "metadata": { - "description": "Required. The preference value for this MX record." + "description": "Required. Properties of private link service connection." } } + }, + "metadata": { + "__bicep_export!": true } }, - "nullable": true, - "metadata": { - "description": "Optional. The list of MX records in the record set." - } - } - } - }, - "nullable": true - }, - "ptrType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } - }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "ptrRecords": { - "type": "array", - "items": { + "customDnsConfigType": { "type": "object", "properties": { - "ptrdname": { + "fqdn": { "type": "string", + "nullable": true, "metadata": { - "description": "Required. The PTR target domain name for this PTR record." + "description": "Optional. FQDN that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." } } + }, + "metadata": { + "__bicep_export!": true } }, - "nullable": true, - "metadata": { - "description": "Optional. The list of PTR records in the record set." - } - } - } - }, - "nullable": true - }, - "soaType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } - }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "soaRecord": { - "type": "object", - "properties": { - "email": { - "type": "string", - "metadata": { - "description": "Required. The email contact for this SOA record." - } - }, - "expireTime": { - "type": "int", - "metadata": { - "description": "Required. The expire time for this SOA record." - } - }, - "host": { - "type": "string", - "metadata": { - "description": "Required. The domain name of the authoritative name server for this SOA record." - } - }, - "minimumTtl": { - "type": "int", - "metadata": { - "description": "Required. The minimum value for this SOA record. By convention this is used to determine the negative caching duration." + "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." + } } }, - "refreshTime": { - "type": "int", - "metadata": { - "description": "Required. The refresh value for this SOA record." + "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.5.1" } - }, - "retryTime": { - "type": "int", - "metadata": { - "description": "Required. The retry time for this SOA record." + } + }, + "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." + } } }, - "serialNumber": { - "type": "int", - "metadata": { - "description": "Required. The serial number for this SOA record." + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "private-dns-zone-group/main.bicep" } } }, - "nullable": true, - "metadata": { - "description": "Optional. The SOA record in the record set." - } - } - } - }, - "nullable": true - }, - "srvType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } - }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "srvRecords": { - "type": "array", - "items": { + "roleAssignmentType": { "type": "object", "properties": { - "priority": { - "type": "int", + "name": { + "type": "string", + "nullable": true, "metadata": { - "description": "Required. The priority value for this SRV record." + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." } }, - "weight": { - "type": "int", + "roleDefinitionIdOrName": { + "type": "string", "metadata": { - "description": "Required. The weight value for this SRV record." + "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'." } }, - "port": { - "type": "int", + "principalId": { + "type": "string", "metadata": { - "description": "Required. The port value for this SRV record." + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." } }, - "target": { + "principalType": { "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, "metadata": { - "description": "Required. The target domain name for this SRV record." + "description": "Optional. The principal type of the assigned principal ID." } - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. The list of SRV records in the record set." - } - } - } - }, - "nullable": true - }, - "txtType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } - }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "txtRecords": { - "type": "array", - "items": { - "type": "object", - "properties": { - "value": { - "type": "array", - "items": { - "type": "string" - }, + }, + "description": { + "type": "string", + "nullable": true, "metadata": { - "description": "Required. The text value of this TXT record." + "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.5.1" + } } - }, - "nullable": true, - "metadata": { - "description": "Optional. The list of TXT records in the record set." - } - } - } - }, - "nullable": true - }, - "virtualNetworkLinkType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "minLength": 1, - "maxLength": 80, - "metadata": { - "description": "Optional. The resource name." - } - }, - "virtualNetworkResourceId": { - "type": "string", - "metadata": { - "description": "Required. The resource ID of the virtual network to link." - } - }, - "location": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The Azure Region where the resource lives." } }, - "registrationEnabled": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Is auto-registration of virtual machine records in the virtual network in the Private DNS zone enabled?." - } - }, - "tags": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Resource tags." - } - }, - "resolutionPolicy": { - "type": "string", - "allowedValues": [ - "Default", - "NxDomainRedirect" - ], - "nullable": true, - "metadata": { - "description": "Optional. The resolution type of the private-dns-zone fallback machanism." - } - } - } - }, - "nullable": true - } - }, - "parameters": { - "name": { - "type": "string", - "metadata": { - "description": "Required. Private DNS zone name." - } - }, - "a": { - "$ref": "#/definitions/aType", - "metadata": { - "description": "Optional. Array of A records." - } - }, - "aaaa": { - "$ref": "#/definitions/aaaaType", - "metadata": { - "description": "Optional. Array of AAAA records." - } - }, - "cname": { - "$ref": "#/definitions/cnameType", - "metadata": { - "description": "Optional. Array of CNAME records." - } - }, - "mx": { - "$ref": "#/definitions/mxType", - "metadata": { - "description": "Optional. Array of MX records." - } - }, - "ptr": { - "$ref": "#/definitions/ptrType", - "metadata": { - "description": "Optional. Array of PTR records." - } - }, - "soa": { - "$ref": "#/definitions/soaType", - "metadata": { - "description": "Optional. Array of SOA records." - } - }, - "srv": { - "$ref": "#/definitions/srvType", - "metadata": { - "description": "Optional. Array of SRV records." - } - }, - "txt": { - "$ref": "#/definitions/txtType", - "metadata": { - "description": "Optional. Array of TXT records." - } - }, - "virtualNetworkLinks": { - "$ref": "#/definitions/virtualNetworkLinkType", - "metadata": { - "description": "Optional. Array of custom objects describing vNet links of the DNS zone. Each object should contain properties 'virtualNetworkResourceId' and 'registrationEnabled'. The 'vnetResourceId' is a resource ID of a vNet to link, 'registrationEnabled' (bool) enables automatic DNS registration in the zone for the linked vNet." - } - }, - "location": { - "type": "string", - "defaultValue": "global", - "metadata": { - "description": "Optional. The location of the PrivateDNSZone. Should be global." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "tags": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Tags of the resource." - } - }, - "lock": { - "$ref": "#/definitions/lockType", - "metadata": { - "description": "Optional. The lock settings of the service." - } - }, - "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')]", - "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": "2024-03-01", - "name": "[format('46d3xbcp.res.network-privatednszone.{0}.{1}', replace('0.7.0', '.', '-'), 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" - } - } - } - } - }, - "privateDnsZone": { - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('name')]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]" - }, - "privateDnsZone_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/privateDnsZones/{0}', parameters('name'))]", - "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", - "properties": { - "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", - "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" - }, - "dependsOn": [ - "privateDnsZone" - ] - }, - "privateDnsZone_roleAssignments": { - "copy": { - "name": "privateDnsZone_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}', parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones', 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": [ - "privateDnsZone" - ] - }, - "privateDnsZone_A": { - "copy": { - "name": "privateDnsZone_A", - "count": "[length(coalesce(parameters('a'), createArray()))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-ARecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "privateDnsZoneName": { - "value": "[parameters('name')]" - }, - "name": { - "value": "[coalesce(parameters('a'), createArray())[copyIndex()].name]" - }, - "aRecords": { - "value": "[tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'aRecords')]" - }, - "metadata": { - "value": "[tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'metadata')]" - }, - "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'ttl'), 3600)]" - }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'roleAssignments')]" - } - }, - "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.32.4.45862", - "templateHash": "2531120132215940282" + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the private endpoint resource to create." + } }, - "name": "Private DNS Zone A record", - "description": "This module deploys a Private DNS Zone A record.", - "owner": "Azure/module-maintainers" - }, - "definitions": { - "roleAssignmentType": { + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + } + }, + "applicationSecurityGroupResourceIds": { "type": "array", "items": { - "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." - } - } - } + "type": "string" }, - "nullable": true - } - }, - "parameters": { - "privateDnsZoneName": { - "type": "string", + "nullable": true, "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + "description": "Optional. Application security groups in which the private endpoint IP configuration is included." } }, - "name": { + "customNetworkInterfaceName": { "type": "string", + "nullable": true, "metadata": { - "description": "Required. The name of the A record." + "description": "Optional. The custom name of the network interface attached to the private endpoint." } }, - "aRecords": { + "ipConfigurations": { "type": "array", + "items": { + "$ref": "#/definitions/ipConfigurationType" + }, "nullable": true, "metadata": { - "description": "Optional. The list of A records in the record set." + "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." } }, - "metadata": { - "type": "object", + "privateDnsZoneGroup": { + "$ref": "#/definitions/privateDnsZoneGroupType", "nullable": true, "metadata": { - "description": "Optional. The metadata attached to the record set." + "description": "Optional. The private DNS zone group to configure for the private endpoint." } }, - "ttl": { - "type": "int", - "defaultValue": 3600, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." + "description": "Optional. Location for all Resources." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "nullable": true, + "metadata": { + "description": "Optional. The lock settings of the service." } }, "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, "metadata": { "description": "Optional. Array of role assignments to create." } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/customDnsConfigType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Custom DNS configurations." + } + }, + "manualPrivateLinkServiceConnections": { + "type": "array", + "items": { + "$ref": "#/definitions/privateLinkServiceConnectionType" + }, + "nullable": true, + "metadata": { + "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." + } + }, + "privateLinkServiceConnections": { + "type": "array", + "items": { + "$ref": "#/definitions/privateLinkServiceConnectionType" + }, + "nullable": true, + "metadata": { + "description": "Conditional. A grouping of information about the connection to the remote resource. Required if `manualPrivateLinkServiceConnections` is empty." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } } }, "variables": { @@ -19313,40 +16674,87 @@ ], "builtInRoleNames": { "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "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')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" } }, "resources": { - "privateDnsZone": { - "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.10.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" + } + } + } + } }, - "A": { - "type": "Microsoft.Network/privateDnsZones/A", - "apiVersion": "2020-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "privateEndpoint": { + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-11-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", "properties": { - "aRecords": "[parameters('aRecords')]", - "metadata": "[parameters('metadata')]", - "ttl": "[parameters('ttl')]" + "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')]" + } } }, - "A_roleAssignments": { + "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": "[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": "A_roleAssignments", + "name": "privateEndpoint_roleAssignments", "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/A/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/A', parameters('privateDnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "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]", @@ -19357,1350 +16765,1335 @@ "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" }, "dependsOn": [ - "A" + "privateEndpoint" ] - } - }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the deployed A record." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the deployed A record." - }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/A', parameters('privateDnsZoneName'), parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group of the deployed A record." - }, - "value": "[resourceGroup().name]" - } - } - } - }, - "dependsOn": [ - "privateDnsZone" - ] - }, - "privateDnsZone_AAAA": { - "copy": { - "name": "privateDnsZone_AAAA", - "count": "[length(coalesce(parameters('aaaa'), createArray()))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-AAAARecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "privateDnsZoneName": { - "value": "[parameters('name')]" - }, - "name": { - "value": "[coalesce(parameters('aaaa'), createArray())[copyIndex()].name]" - }, - "aaaaRecords": { - "value": "[tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'aaaaRecords')]" - }, - "metadata": { - "value": "[tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'metadata')]" - }, - "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'ttl'), 3600)]" - }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'roleAssignments')]" - } - }, - "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.32.4.45862", - "templateHash": "16709340450244912125" }, - "name": "Private DNS Zone AAAA record", - "description": "This module deploys a Private DNS Zone AAAA record.", - "owner": "Azure/module-maintainers" - }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "type": "object", - "properties": { + "privateEndpoint_privateDnsZoneGroup": { + "condition": "[not(empty(parameters('privateDnsZoneGroup')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateEndpoint-PrivateDnsZoneGroup', uniqueString(deployment().name))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." - } + "value": "[tryGet(parameters('privateDnsZoneGroup'), 'name')]" }, - "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'." - } + "privateEndpointName": { + "value": "[parameters('name')]" }, - "principalId": { - "type": "string", - "metadata": { - "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." - } + "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.33.13.18514", + "templateHash": "5440815542537978381" + }, + "name": "Private Endpoint Private DNS Zone Groups", + "description": "This module deploys a Private Endpoint Private DNS Zone Group." }, - "principalType": { - "type": "string", - "allowedValues": [ - "Device", - "ForeignGroup", - "Group", - "ServicePrincipal", - "User" - ], - "nullable": true, - "metadata": { - "description": "Optional. The principal type of the assigned principal ID." + "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": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The description of the role assignment." + "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." + } } }, - "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\"." - } + "variables": { + "copy": [ + { + "name": "privateDnsZoneConfigsVar", + "count": "[length(parameters('privateDnsZoneConfigs'))]", + "input": { + "name": "[coalesce(tryGet(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')], 'name'), last(split(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId, '/')))]", + "properties": { + "privateDnsZoneId": "[parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId]" + } + } + } + ] }, - "conditionVersion": { - "type": "string", - "allowedValues": [ - "2.0" - ], - "nullable": true, - "metadata": { - "description": "Optional. Version of the condition." + "resources": { + "privateEndpoint": { + "existing": true, + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-11-01", + "name": "[parameters('privateEndpointName')]" + }, + "privateDnsZoneGroup": { + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2023-11-01", + "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", + "properties": { + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" + } } }, - "delegatedManagedIdentityResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The Resource Id of the delegated managed identity resource." + "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]" } } } }, - "nullable": true + "dependsOn": [ + "privateEndpoint" + ] } }, - "parameters": { - "privateDnsZoneName": { + "outputs": { + "resourceGroupName": { "type": "string", "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." - } + "description": "The resource group the private endpoint was deployed into." + }, + "value": "[resourceGroup().name]" }, - "name": { + "resourceId": { "type": "string", "metadata": { - "description": "Required. The name of the AAAA record." - } - }, - "aaaaRecords": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. The list of AAAA records in the record set." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata attached to the record set." - } + "description": "The resource ID of the private endpoint." + }, + "value": "[resourceId('Microsoft.Network/privateEndpoints', parameters('name'))]" }, - "ttl": { - "type": "int", - "defaultValue": 3600, + "name": { + "type": "string", "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." - } + "description": "The name of the private endpoint." + }, + "value": "[parameters('name')]" }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", + "location": { + "type": "string", "metadata": { - "description": "Optional. Array of role assignments to create." - } - } - }, - "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')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", - "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" - } - }, - "resources": { - "privateDnsZone": { - "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" - }, - "AAAA": { - "type": "Microsoft.Network/privateDnsZones/AAAA", - "apiVersion": "2020-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "properties": { - "aaaaRecords": "[parameters('aaaaRecords')]", - "metadata": "[parameters('metadata')]", - "ttl": "[parameters('ttl')]" - } - }, - "AAAA_roleAssignments": { - "copy": { - "name": "AAAA_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + "description": "The location the resource was deployed into." }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/AAAA/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/AAAA', parameters('privateDnsZoneName'), 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')]" + "value": "[reference('privateEndpoint', '2023-11-01', 'full').location]" + }, + "customDnsConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/customDnsConfigType" }, - "dependsOn": [ - "AAAA" - ] - } - }, - "outputs": { - "name": { - "type": "string", "metadata": { - "description": "The name of the deployed AAAA record." + "description": "The custom DNS configurations of the private endpoint." }, - "value": "[parameters('name')]" + "value": "[reference('privateEndpoint').customDnsConfigs]" }, - "resourceId": { - "type": "string", + "networkInterfaceResourceIds": { + "type": "array", + "items": { + "type": "string" + }, "metadata": { - "description": "The resource ID of the deployed AAAA record." + "description": "The resource IDs of the network interfaces associated with the private endpoint." }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/AAAA', parameters('privateDnsZoneName'), parameters('name'))]" + "value": "[map(reference('privateEndpoint').networkInterfaces, lambda('nic', lambdaVariables('nic').id))]" }, - "resourceGroupName": { + "groupId": { "type": "string", + "nullable": true, "metadata": { - "description": "The resource group of the deployed AAAA record." + "description": "The group Id for the private endpoint Group." }, - "value": "[resourceGroup().name]" + "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": [ - "privateDnsZone" + "keyVault" ] + } + }, + "outputs": { + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the key vault." + }, + "value": "[resourceId('Microsoft.KeyVault/vaults', parameters('name'))]" }, - "privateDnsZone_CNAME": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the key vault was created in." + }, + "value": "[resourceGroup().name]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the key vault." + }, + "value": "[parameters('name')]" + }, + "uri": { + "type": "string", + "metadata": { + "description": "The URI of the key vault." + }, + "value": "[reference('keyVault').vaultUri]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('keyVault', '2022-07-01', 'full').location]" + }, + "privateEndpoints": { + "type": "array", + "items": { + "$ref": "#/definitions/privateEndpointOutputType" + }, + "metadata": { + "description": "The private endpoints of the key vault." + }, "copy": { - "name": "privateDnsZone_CNAME", - "count": "[length(coalesce(parameters('cname'), createArray()))]" + "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]", + "input": { + "name": "[reference(format('keyVault_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", + "resourceId": "[reference(format('keyVault_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", + "groupId": "[tryGet(tryGet(reference(format('keyVault_privateEndpoints[{0}]', copyIndex())).outputs, 'groupId'), 'value')]", + "customDnsConfigs": "[reference(format('keyVault_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfigs.value]", + "networkInterfaceResourceIds": "[reference(format('keyVault_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceResourceIds.value]" + } + } + }, + "secrets": { + "type": "array", + "items": { + "$ref": "#/definitions/credentialOutputType" }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-CNAMERecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "privateDnsZoneName": { - "value": "[parameters('name')]" - }, - "name": { - "value": "[coalesce(parameters('cname'), createArray())[copyIndex()].name]" - }, - "cnameRecord": { - "value": "[tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'cnameRecord')]" - }, - "metadata": { - "value": "[tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'metadata')]" - }, - "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'ttl'), 3600)]" - }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'roleAssignments')]" - } - }, - "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.32.4.45862", - "templateHash": "9976020649752073181" - }, - "name": "Private DNS Zone CNAME record", - "description": "This module deploys a Private DNS Zone CNAME record.", - "owner": "Azure/module-maintainers" - }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } - } - } - }, - "nullable": true + "metadata": { + "description": "The properties of the created secrets." + }, + "copy": { + "count": "[length(range(0, length(coalesce(parameters('secrets'), createArray()))))]", + "input": { + "resourceId": "[reference(format('keyVault_secrets[{0}]', range(0, length(coalesce(parameters('secrets'), createArray())))[copyIndex()])).outputs.resourceId.value]", + "uri": "[reference(format('keyVault_secrets[{0}]', range(0, length(coalesce(parameters('secrets'), createArray())))[copyIndex()])).outputs.secretUri.value]", + "uriWithVersion": "[reference(format('keyVault_secrets[{0}]', range(0, length(coalesce(parameters('secrets'), createArray())))[copyIndex()])).outputs.secretUriWithVersion.value]" + } + } + }, + "keys": { + "type": "array", + "items": { + "$ref": "#/definitions/credentialOutputType" + }, + "metadata": { + "description": "The properties of the created keys." + }, + "copy": { + "count": "[length(range(0, length(coalesce(parameters('keys'), createArray()))))]", + "input": { + "resourceId": "[reference(format('keyVault_keys[{0}]', range(0, length(coalesce(parameters('keys'), createArray())))[copyIndex()])).outputs.resourceId.value]", + "uri": "[reference(format('keyVault_keys[{0}]', range(0, length(coalesce(parameters('keys'), createArray())))[copyIndex()])).outputs.keyUri.value]", + "uriWithVersion": "[reference(format('keyVault_keys[{0}]', range(0, length(coalesce(parameters('keys'), createArray())))[copyIndex()])).outputs.keyUriWithVersion.value]" + } + } + } + } + } + }, + "dependsOn": [ + "privateDnsZone" + ] + } + }, + "outputs": { + "resourceId": { + "type": "string", + "value": "[reference('keyvault').outputs.resourceId.value]" + }, + "name": { + "type": "string", + "value": "[reference('keyvault').outputs.name.value]" + } + } + } + }, + "dependsOn": [ + "appIdentity", + "logAnalyticsWorkspace", + "network" + ] + }, + "containerRegistry": { + "condition": "[parameters('acrEnabled')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('{0}-container-registry-deployment', parameters('name')), 64)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[format('cr{0}{1}', parameters('name'), variables('resourceToken'))]" + }, + "location": { + "value": "[parameters('location')]" + }, + "networkIsolation": { + "value": "[parameters('networkIsolation')]" + }, + "virtualNetworkResourceId": "[if(parameters('networkIsolation'), createObject('value', reference('network').outputs.resourceId.value), createObject('value', ''))]", + "virtualNetworkSubnetResourceId": "[if(parameters('networkIsolation'), createObject('value', reference('network').outputs.defaultSubnetResourceId.value), createObject('value', ''))]", + "logAnalyticsWorkspaceResourceId": { + "value": "[reference('logAnalyticsWorkspace').outputs.resourceId.value]" + }, + "tags": { + "value": "[variables('allTags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.36.1.42791", + "templateHash": "2158520837294746606" + } + }, + "parameters": { + "name": { + "type": "string", + "minLength": 5, + "metadata": { + "description": "Name of the Container Registry." + } + }, + "location": { + "type": "string", + "metadata": { + "description": "Specifies the location for all the Azure resources." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Tags to be applied to the resources." + } + }, + "virtualNetworkResourceId": { + "type": "string", + "metadata": { + "description": "Resource ID of the virtual network to link the private DNS zones." + } + }, + "virtualNetworkSubnetResourceId": { + "type": "string", + "metadata": { + "description": "Resource ID of the subnet for the private endpoint." + } + }, + "logAnalyticsWorkspaceResourceId": { + "type": "string", + "metadata": { + "description": "Resource ID of the Log Analytics workspace to use for diagnostic settings." + } + }, + "networkIsolation": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Specifies whether network isolation is enabled. This will create a private endpoint for the Container Registry and link the private DNS zone." + } + } + }, + "variables": { + "nameFormatted": "[take(toLower(parameters('name')), 50)]" + }, + "resources": [ + { + "condition": "[parameters('networkIsolation')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "private-dns-acr-deployment", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[format('privatelink.{0}', if(equals(toLower(environment().name), 'azureusgovernment'), 'azurecr.us', 'azurecr.io'))]" + }, + "virtualNetworkLinks": { + "value": [ + { + "virtualNetworkResourceId": "[parameters('virtualNetworkResourceId')]" + } + ] + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "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.32.4.45862", + "templateHash": "83178825086050429" + }, + "name": "Private DNS Zones", + "description": "This module deploys a Private DNS zone.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "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." } }, - "parameters": { - "privateDnsZoneName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the CNAME record." - } - }, - "cnameRecord": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. A CNAME record." - } - }, + "roleDefinitionIdOrName": { + "type": "string", "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata attached to the record set." - } - }, - "ttl": { - "type": "int", - "defaultValue": 3600, - "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } + "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'." } }, - "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)))))]" - } + "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" ], - "builtInRoleNames": { - "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", - "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." } }, - "resources": { - "privateDnsZone": { - "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" - }, - "CNAME": { - "type": "Microsoft.Network/privateDnsZones/CNAME", - "apiVersion": "2020-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "properties": { - "cnameRecord": "[parameters('cnameRecord')]", - "metadata": "[parameters('metadata')]", - "ttl": "[parameters('ttl')]" - } - }, - "CNAME_roleAssignments": { - "copy": { - "name": "CNAME_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/CNAME/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/CNAME', parameters('privateDnsZoneName'), 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": [ - "CNAME" - ] + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." } }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the deployed CNAME record." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the deployed CNAME record." - }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/CNAME', parameters('privateDnsZoneName'), parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group of the deployed CNAME record." - }, - "value": "[resourceGroup().name]" + "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." } } } }, - "dependsOn": [ - "privateDnsZone" - ] + "nullable": true }, - "privateDnsZone_MX": { - "copy": { - "name": "privateDnsZone_MX", - "count": "[length(coalesce(parameters('mx'), createArray()))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-MXRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "lockType": { + "type": "object", "properties": { - "expressionEvaluationOptions": { - "scope": "inner" + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } }, - "mode": "Incremental", - "parameters": { - "privateDnsZoneName": { - "value": "[parameters('name')]" - }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "nullable": true + }, + "aType": { + "type": "array", + "items": { + "type": "object", + "properties": { "name": { - "value": "[coalesce(parameters('mx'), createArray())[copyIndex()].name]" + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } }, "metadata": { - "value": "[tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'metadata')]" - }, - "mxRecords": { - "value": "[tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'mxRecords')]" + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } }, "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'ttl'), 3600)]" + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } }, "roleAssignments": { - "value": "[tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'roleAssignments')]" - } - }, - "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.32.4.45862", - "templateHash": "2520323624213076361" - }, - "name": "Private DNS Zone MX record", - "description": "This module deploys a Private DNS Zone MX record.", - "owner": "Azure/module-maintainers" + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } + "aRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "ipv4Address": { + "type": "string", + "metadata": { + "description": "Required. The IPv4 address of this A record." } } - }, - "nullable": true - } - }, - "parameters": { - "privateDnsZoneName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the MX record." } }, + "nullable": true, "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata attached to the record set." - } - }, - "mxRecords": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. The list of MX records in the record set." - } - }, - "ttl": { - "type": "int", - "defaultValue": 3600, - "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } + "description": "Optional. The list of A records in the record set." + } + } + } + }, + "nullable": true + }, + "aaaaType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." } }, - "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')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", - "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." } }, - "resources": { - "privateDnsZone": { - "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" - }, - "MX": { - "type": "Microsoft.Network/privateDnsZones/MX", - "apiVersion": "2020-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "properties": { - "metadata": "[parameters('metadata')]", - "mxRecords": "[parameters('mxRecords')]", - "ttl": "[parameters('ttl')]" - } - }, - "MX_roleAssignments": { - "copy": { - "name": "MX_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/MX/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/MX', parameters('privateDnsZoneName'), 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": [ - "MX" - ] + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." } }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the deployed MX record." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the deployed MX record." - }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/MX', parameters('privateDnsZoneName'), parameters('name'))]" + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "aaaaRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "ipv6Address": { + "type": "string", + "metadata": { + "description": "Required. The IPv6 address of this AAAA record." + } + } + } }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group of the deployed MX record." - }, - "value": "[resourceGroup().name]" + "nullable": true, + "metadata": { + "description": "Optional. The list of AAAA records in the record set." } } } }, - "dependsOn": [ - "privateDnsZone" - ] + "nullable": true }, - "privateDnsZone_PTR": { - "copy": { - "name": "privateDnsZone_PTR", - "count": "[length(coalesce(parameters('ptr'), createArray()))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-PTRRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "privateDnsZoneName": { - "value": "[parameters('name')]" - }, + "cnameType": { + "type": "array", + "items": { + "type": "object", + "properties": { "name": { - "value": "[coalesce(parameters('ptr'), createArray())[copyIndex()].name]" + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } }, "metadata": { - "value": "[tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'metadata')]" - }, - "ptrRecords": { - "value": "[tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'ptrRecords')]" + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } }, "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'ttl'), 3600)]" + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } }, "roleAssignments": { - "value": "[tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'roleAssignments')]" - } - }, - "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.32.4.45862", - "templateHash": "3080404733048745471" - }, - "name": "Private DNS Zone PTR record", - "description": "This module deploys a Private DNS Zone PTR record.", - "owner": "Azure/module-maintainers" - }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } - } - } - }, - "nullable": true + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." } }, - "parameters": { - "privateDnsZoneName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the PTR record." + "cnameRecord": { + "type": "object", + "properties": { + "cname": { + "type": "string", + "metadata": { + "description": "Required. The canonical name of the CNAME record." + } } }, + "nullable": true, "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata attached to the record set." - } - }, - "ptrRecords": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. The list of PTR records in the record set." - } - }, - "ttl": { - "type": "int", - "defaultValue": 3600, - "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } + "description": "Optional. The CNAME record in the record set." + } + } + } + }, + "nullable": true + }, + "mxType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." } }, - "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')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", - "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." } }, - "resources": { - "privateDnsZone": { - "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" - }, - "PTR": { - "type": "Microsoft.Network/privateDnsZones/PTR", - "apiVersion": "2020-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "mxRecords": { + "type": "array", + "items": { + "type": "object", "properties": { - "metadata": "[parameters('metadata')]", - "ptrRecords": "[parameters('ptrRecords')]", - "ttl": "[parameters('ttl')]" + "exchange": { + "type": "string", + "metadata": { + "description": "Required. The domain name of the mail host for this MX record." + } + }, + "preference": { + "type": "int", + "metadata": { + "description": "Required. The preference value for this MX record." + } + } } }, - "PTR_roleAssignments": { - "copy": { - "name": "PTR_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/PTR/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/PTR', parameters('privateDnsZoneName'), 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": [ - "PTR" - ] - } - }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the deployed PTR record." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the deployed PTR record." - }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/PTR', parameters('privateDnsZoneName'), parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group of the deployed PTR record." - }, - "value": "[resourceGroup().name]" + "nullable": true, + "metadata": { + "description": "Optional. The list of MX records in the record set." } } } }, - "dependsOn": [ - "privateDnsZone" - ] + "nullable": true }, - "privateDnsZone_SOA": { - "copy": { - "name": "privateDnsZone_SOA", - "count": "[length(coalesce(parameters('soa'), createArray()))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-SOARecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "privateDnsZoneName": { - "value": "[parameters('name')]" - }, + "ptrType": { + "type": "array", + "items": { + "type": "object", + "properties": { "name": { - "value": "[coalesce(parameters('soa'), createArray())[copyIndex()].name]" + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } }, "metadata": { - "value": "[tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'metadata')]" - }, - "soaRecord": { - "value": "[tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'soaRecord')]" + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } }, "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'ttl'), 3600)]" + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } }, "roleAssignments": { - "value": "[tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'roleAssignments')]" - } - }, - "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.32.4.45862", - "templateHash": "6653951445614700931" - }, - "name": "Private DNS Zone SOA record", - "description": "This module deploys a Private DNS Zone SOA record.", - "owner": "Azure/module-maintainers" + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } + "ptrRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "ptrdname": { + "type": "string", + "metadata": { + "description": "Required. The PTR target domain name for this PTR record." } } - }, - "nullable": true - } - }, - "parameters": { - "privateDnsZoneName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the SOA record." } }, + "nullable": true, "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata attached to the record set." - } - }, - "soaRecord": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. A SOA record." - } - }, - "ttl": { - "type": "int", - "defaultValue": 3600, - "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } + "description": "Optional. The list of PTR records in the record set." + } + } + } + }, + "nullable": true + }, + "soaType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." } }, - "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')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", - "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." } }, - "resources": { - "privateDnsZone": { - "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" - }, - "SOA": { - "type": "Microsoft.Network/privateDnsZones/SOA", - "apiVersion": "2020-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "properties": { - "metadata": "[parameters('metadata')]", - "soaRecord": "[parameters('soaRecord')]", - "ttl": "[parameters('ttl')]" - } - }, - "SOA_roleAssignments": { - "copy": { - "name": "SOA_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/SOA/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/SOA', parameters('privateDnsZoneName'), 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": [ - "SOA" - ] + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." } }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the deployed SOA record." + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "soaRecord": { + "type": "object", + "properties": { + "email": { + "type": "string", + "metadata": { + "description": "Required. The email contact for this SOA record." + } }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the deployed SOA record." + "expireTime": { + "type": "int", + "metadata": { + "description": "Required. The expire time for this SOA record." + } }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/SOA', parameters('privateDnsZoneName'), parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group of the deployed SOA record." + "host": { + "type": "string", + "metadata": { + "description": "Required. The domain name of the authoritative name server for this SOA record." + } }, - "value": "[resourceGroup().name]" + "minimumTtl": { + "type": "int", + "metadata": { + "description": "Required. The minimum value for this SOA record. By convention this is used to determine the negative caching duration." + } + }, + "refreshTime": { + "type": "int", + "metadata": { + "description": "Required. The refresh value for this SOA record." + } + }, + "retryTime": { + "type": "int", + "metadata": { + "description": "Required. The retry time for this SOA record." + } + }, + "serialNumber": { + "type": "int", + "metadata": { + "description": "Required. The serial number for this SOA record." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The SOA record in the record set." } } } }, - "dependsOn": [ - "privateDnsZone" - ] + "nullable": true }, - "privateDnsZone_SRV": { - "copy": { - "name": "privateDnsZone_SRV", - "count": "[length(coalesce(parameters('srv'), createArray()))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-SRVRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "privateDnsZoneName": { - "value": "[parameters('name')]" - }, + "srvType": { + "type": "array", + "items": { + "type": "object", + "properties": { "name": { - "value": "[coalesce(parameters('srv'), createArray())[copyIndex()].name]" + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } }, "metadata": { - "value": "[tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'metadata')]" - }, - "srvRecords": { - "value": "[tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'srvRecords')]" + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } }, "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'ttl'), 3600)]" + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } }, "roleAssignments": { - "value": "[tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'roleAssignments')]" + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "srvRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "priority": { + "type": "int", + "metadata": { + "description": "Required. The priority value for this SRV record." + } + }, + "weight": { + "type": "int", + "metadata": { + "description": "Required. The weight value for this SRV record." + } + }, + "port": { + "type": "int", + "metadata": { + "description": "Required. The port value for this SRV record." + } + }, + "target": { + "type": "string", + "metadata": { + "description": "Required. The target domain name for this SRV record." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of SRV records in the record set." + } } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "languageVersion": "2.0", - "contentVersion": "1.0.0.0", + } + }, + "nullable": true + }, + "txtType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, "metadata": { - "_generator": { - "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "5790774778713328446" - }, - "name": "Private DNS Zone SRV record", - "description": "This module deploys a Private DNS Zone SRV record.", - "owner": "Azure/module-maintainers" + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "txtRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "value": { + "type": "array", + "items": { + "type": "string" }, - "roleDefinitionIdOrName": { + "metadata": { + "description": "Required. The text value of this TXT record." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of TXT records in the record set." + } + } + } + }, + "nullable": true + }, + "virtualNetworkLinkType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "minLength": 1, + "maxLength": 80, + "metadata": { + "description": "Optional. The resource name." + } + }, + "virtualNetworkResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the virtual network to link." + } + }, + "location": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Azure Region where the resource lives." + } + }, + "registrationEnabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Is auto-registration of virtual machine records in the virtual network in the Private DNS zone enabled?." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Resource tags." + } + }, + "resolutionPolicy": { + "type": "string", + "allowedValues": [ + "Default", + "NxDomainRedirect" + ], + "nullable": true, + "metadata": { + "description": "Optional. The resolution type of the private-dns-zone fallback machanism." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Private DNS zone name." + } + }, + "a": { + "$ref": "#/definitions/aType", + "metadata": { + "description": "Optional. Array of A records." + } + }, + "aaaa": { + "$ref": "#/definitions/aaaaType", + "metadata": { + "description": "Optional. Array of AAAA records." + } + }, + "cname": { + "$ref": "#/definitions/cnameType", + "metadata": { + "description": "Optional. Array of CNAME records." + } + }, + "mx": { + "$ref": "#/definitions/mxType", + "metadata": { + "description": "Optional. Array of MX records." + } + }, + "ptr": { + "$ref": "#/definitions/ptrType", + "metadata": { + "description": "Optional. Array of PTR records." + } + }, + "soa": { + "$ref": "#/definitions/soaType", + "metadata": { + "description": "Optional. Array of SOA records." + } + }, + "srv": { + "$ref": "#/definitions/srvType", + "metadata": { + "description": "Optional. Array of SRV records." + } + }, + "txt": { + "$ref": "#/definitions/txtType", + "metadata": { + "description": "Optional. Array of TXT records." + } + }, + "virtualNetworkLinks": { + "$ref": "#/definitions/virtualNetworkLinkType", + "metadata": { + "description": "Optional. Array of custom objects describing vNet links of the DNS zone. Each object should contain properties 'virtualNetworkResourceId' and 'registrationEnabled'. The 'vnetResourceId' is a resource ID of a vNet to link, 'registrationEnabled' (bool) enables automatic DNS registration in the zone for the linked vNet." + } + }, + "location": { + "type": "string", + "defaultValue": "global", + "metadata": { + "description": "Optional. The location of the PrivateDNSZone. Should be global." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "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')]", + "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": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privatednszone.{0}.{1}', replace('0.7.0', '.', '-'), 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" + } + } + } + } + }, + "privateDnsZone": { + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]" + }, + "privateDnsZone_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/privateDnsZones/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "privateDnsZone_roleAssignments": { + "copy": { + "name": "privateDnsZone_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones', 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": [ + "privateDnsZone" + ] + }, + "privateDnsZone_A": { + "copy": { + "name": "privateDnsZone_A", + "count": "[length(coalesce(parameters('a'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-ARecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('a'), createArray())[copyIndex()].name]" + }, + "aRecords": { + "value": "[tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'aRecords')]" + }, + "metadata": { + "value": "[tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'metadata')]" + }, + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'ttl'), 3600)]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'roleAssignments')]" + } + }, + "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.32.4.45862", + "templateHash": "2531120132215940282" + }, + "name": "Private DNS Zone A record", + "description": "This module deploys a Private DNS Zone A record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "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'." @@ -20772,21 +18165,21 @@ "name": { "type": "string", "metadata": { - "description": "Required. The name of the SRV record." + "description": "Required. The name of the A record." } }, - "metadata": { - "type": "object", + "aRecords": { + "type": "array", "nullable": true, "metadata": { - "description": "Optional. The metadata attached to the record set." + "description": "Optional. The list of A records in the record set." } }, - "srvRecords": { - "type": "array", + "metadata": { + "type": "object", "nullable": true, "metadata": { - "description": "Optional. The list of SRV records in the record set." + "description": "Optional. The metadata attached to the record set." } }, "ttl": { @@ -20828,25 +18221,25 @@ "apiVersion": "2020-06-01", "name": "[parameters('privateDnsZoneName')]" }, - "SRV": { - "type": "Microsoft.Network/privateDnsZones/SRV", + "A": { + "type": "Microsoft.Network/privateDnsZones/A", "apiVersion": "2020-06-01", "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", "properties": { + "aRecords": "[parameters('aRecords')]", "metadata": "[parameters('metadata')]", - "srvRecords": "[parameters('srvRecords')]", "ttl": "[parameters('ttl')]" } }, - "SRV_roleAssignments": { + "A_roleAssignments": { "copy": { - "name": "SRV_roleAssignments", + "name": "A_roleAssignments", "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/SRV/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/SRV', parameters('privateDnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/A/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/A', parameters('privateDnsZoneName'), 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]", @@ -20857,7 +18250,7 @@ "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" }, "dependsOn": [ - "SRV" + "A" ] } }, @@ -20865,21 +18258,21 @@ "name": { "type": "string", "metadata": { - "description": "The name of the deployed SRV record." + "description": "The name of the deployed A record." }, "value": "[parameters('name')]" }, "resourceId": { "type": "string", "metadata": { - "description": "The resource ID of the deployed SRV record." + "description": "The resource ID of the deployed A record." }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/SRV', parameters('privateDnsZoneName'), parameters('name'))]" + "value": "[resourceId('Microsoft.Network/privateDnsZones/A', parameters('privateDnsZoneName'), parameters('name'))]" }, "resourceGroupName": { "type": "string", "metadata": { - "description": "The resource group of the deployed SRV record." + "description": "The resource group of the deployed A record." }, "value": "[resourceGroup().name]" } @@ -20890,14 +18283,14 @@ "privateDnsZone" ] }, - "privateDnsZone_TXT": { + "privateDnsZone_AAAA": { "copy": { - "name": "privateDnsZone_TXT", - "count": "[length(coalesce(parameters('txt'), createArray()))]" + "name": "privateDnsZone_AAAA", + "count": "[length(coalesce(parameters('aaaa'), createArray()))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-TXTRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "name": "[format('{0}-PrivateDnsZone-AAAARecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -20908,19 +18301,19 @@ "value": "[parameters('name')]" }, "name": { - "value": "[coalesce(parameters('txt'), createArray())[copyIndex()].name]" + "value": "[coalesce(parameters('aaaa'), createArray())[copyIndex()].name]" }, - "metadata": { - "value": "[tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'metadata')]" + "aaaaRecords": { + "value": "[tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'aaaaRecords')]" }, - "txtRecords": { - "value": "[tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'txtRecords')]" + "metadata": { + "value": "[tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'metadata')]" }, "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'ttl'), 3600)]" + "value": "[coalesce(tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'ttl'), 3600)]" }, "roleAssignments": { - "value": "[tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'roleAssignments')]" + "value": "[tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'roleAssignments')]" } }, "template": { @@ -20931,10 +18324,10 @@ "_generator": { "name": "bicep", "version": "0.32.4.45862", - "templateHash": "1855369119498044639" + "templateHash": "16709340450244912125" }, - "name": "Private DNS Zone TXT record", - "description": "This module deploys a Private DNS Zone TXT record.", + "name": "Private DNS Zone AAAA record", + "description": "This module deploys a Private DNS Zone AAAA record.", "owner": "Azure/module-maintainers" }, "definitions": { @@ -21022,7 +18415,14 @@ "name": { "type": "string", "metadata": { - "description": "Required. The name of the TXT record." + "description": "Required. The name of the AAAA record." + } + }, + "aaaaRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of AAAA records in the record set." } }, "metadata": { @@ -21039,13 +18439,6 @@ "description": "Optional. The TTL (time-to-live) of the records in the record set." } }, - "txtRecords": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. The list of TXT records in the record set." - } - }, "roleAssignments": { "$ref": "#/definitions/roleAssignmentType", "metadata": { @@ -21078,25 +18471,25 @@ "apiVersion": "2020-06-01", "name": "[parameters('privateDnsZoneName')]" }, - "TXT": { - "type": "Microsoft.Network/privateDnsZones/TXT", + "AAAA": { + "type": "Microsoft.Network/privateDnsZones/AAAA", "apiVersion": "2020-06-01", "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", "properties": { + "aaaaRecords": "[parameters('aaaaRecords')]", "metadata": "[parameters('metadata')]", - "ttl": "[parameters('ttl')]", - "txtRecords": "[parameters('txtRecords')]" + "ttl": "[parameters('ttl')]" } }, - "TXT_roleAssignments": { + "AAAA_roleAssignments": { "copy": { - "name": "TXT_roleAssignments", + "name": "AAAA_roleAssignments", "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/TXT/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/TXT', parameters('privateDnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/AAAA/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/AAAA', parameters('privateDnsZoneName'), 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]", @@ -21107,7 +18500,7 @@ "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" }, "dependsOn": [ - "TXT" + "AAAA" ] } }, @@ -21115,21 +18508,21 @@ "name": { "type": "string", "metadata": { - "description": "The name of the deployed TXT record." + "description": "The name of the deployed AAAA record." }, "value": "[parameters('name')]" }, "resourceId": { "type": "string", "metadata": { - "description": "The resource ID of the deployed TXT record." + "description": "The resource ID of the deployed AAAA record." }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/TXT', parameters('privateDnsZoneName'), parameters('name'))]" + "value": "[resourceId('Microsoft.Network/privateDnsZones/AAAA', parameters('privateDnsZoneName'), parameters('name'))]" }, "resourceGroupName": { "type": "string", "metadata": { - "description": "The resource group of the deployed TXT record." + "description": "The resource group of the deployed AAAA record." }, "value": "[resourceGroup().name]" } @@ -21140,14 +18533,14 @@ "privateDnsZone" ] }, - "privateDnsZone_virtualNetworkLinks": { + "privateDnsZone_CNAME": { "copy": { - "name": "privateDnsZone_virtualNetworkLinks", - "count": "[length(coalesce(parameters('virtualNetworkLinks'), createArray()))]" + "name": "privateDnsZone_CNAME", + "count": "[length(coalesce(parameters('cname'), createArray()))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-VirtualNetworkLink-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "name": "[format('{0}-PrivateDnsZone-CNAMERecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -21158,22 +18551,19 @@ "value": "[parameters('name')]" }, "name": { - "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'name'), format('{0}-vnetlink', last(split(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()].virtualNetworkResourceId, '/'))))]" - }, - "virtualNetworkResourceId": { - "value": "[coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()].virtualNetworkResourceId]" + "value": "[coalesce(parameters('cname'), createArray())[copyIndex()].name]" }, - "location": { - "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'location'), 'global')]" + "cnameRecord": { + "value": "[tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'cnameRecord')]" }, - "registrationEnabled": { - "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'registrationEnabled'), false())]" + "metadata": { + "value": "[tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'metadata')]" }, - "tags": { - "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'ttl'), 3600)]" }, - "resolutionPolicy": { - "value": "[tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'resolutionPolicy')]" + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'roleAssignments')]" } }, "template": { @@ -21184,111 +18574,207 @@ "_generator": { "name": "bicep", "version": "0.32.4.45862", - "templateHash": "15326596012552051215" + "templateHash": "9976020649752073181" }, - "name": "Private DNS Zone Virtual Network Link", - "description": "This module deploys a Private DNS Zone Virtual Network Link.", + "name": "Private DNS Zone CNAME record", + "description": "This module deploys a Private DNS Zone CNAME record.", "owner": "Azure/module-maintainers" }, - "parameters": { - "privateDnsZoneName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", - "defaultValue": "[format('{0}-vnetlink', last(split(parameters('virtualNetworkResourceId'), '/')))]", - "metadata": { - "description": "Optional. The name of the virtual network link." - } - }, - "location": { - "type": "string", - "defaultValue": "global", - "metadata": { - "description": "Optional. The location of the PrivateDNSZone. Should be global." - } - }, - "tags": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Tags of the resource." - } - }, - "registrationEnabled": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "Optional. Is auto-registration of virtual machine records in the virtual network in the Private DNS zone enabled?." - } - }, - "virtualNetworkResourceId": { - "type": "string", - "metadata": { - "description": "Required. Link to another virtual network resource ID." - } - }, - "resolutionPolicy": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The resolution policy on the virtual network link. Only applicable for virtual network links to privatelink zones, and for A,AAAA,CNAME queries. When set to `NxDomainRedirect`, Azure DNS resolver falls back to public resolution if private dns query resolution results in non-existent domain response. `Default` is configured as the default option." - } - } - }, - "resources": { - "privateDnsZone": { - "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" - }, - "virtualNetworkLink": { - "type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks", - "apiVersion": "2024-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "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." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the CNAME record." + } + }, + "cnameRecord": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. A CNAME record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "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')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "CNAME": { + "type": "Microsoft.Network/privateDnsZones/CNAME", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", "properties": { - "registrationEnabled": "[parameters('registrationEnabled')]", - "virtualNetwork": { - "id": "[parameters('virtualNetworkResourceId')]" - }, - "resolutionPolicy": "[parameters('resolutionPolicy')]" + "cnameRecord": "[parameters('cnameRecord')]", + "metadata": "[parameters('metadata')]", + "ttl": "[parameters('ttl')]" } + }, + "CNAME_roleAssignments": { + "copy": { + "name": "CNAME_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/CNAME/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/CNAME', parameters('privateDnsZoneName'), 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": [ + "CNAME" + ] } }, "outputs": { "name": { "type": "string", "metadata": { - "description": "The name of the deployed virtual network link." + "description": "The name of the deployed CNAME record." }, "value": "[parameters('name')]" }, "resourceId": { "type": "string", "metadata": { - "description": "The resource ID of the deployed virtual network link." + "description": "The resource ID of the deployed CNAME record." }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/virtualNetworkLinks', parameters('privateDnsZoneName'), parameters('name'))]" + "value": "[resourceId('Microsoft.Network/privateDnsZones/CNAME', parameters('privateDnsZoneName'), parameters('name'))]" }, "resourceGroupName": { "type": "string", "metadata": { - "description": "The resource group of the deployed virtual network link." + "description": "The resource group of the deployed CNAME record." }, "value": "[resourceGroup().name]" - }, - "location": { - "type": "string", - "metadata": { - "description": "The location the resource was deployed into." - }, - "value": "[reference('virtualNetworkLink', '2024-06-01', 'full').location]" } } } @@ -21296,924 +18782,538 @@ "dependsOn": [ "privateDnsZone" ] - } - }, - "outputs": { - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group the private DNS zone was deployed into." - }, - "value": "[resourceGroup().name]" - }, - "name": { - "type": "string", - "metadata": { - "description": "The name of the private DNS zone." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the private DNS zone." - }, - "value": "[resourceId('Microsoft.Network/privateDnsZones', parameters('name'))]" - }, - "location": { - "type": "string", - "metadata": { - "description": "The location the resource was deployed into." - }, - "value": "[reference('privateDnsZone', '2020-06-01', 'full').location]" - } - } - } - } - }, - "filePrivateDnsZone": { - "condition": "[parameters('networkIsolation')]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "private-dns-file-deployment", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[format('privatelink.file.{0}', environment().suffixes.storage)]" - }, - "virtualNetworkLinks": { - "value": [ - { - "virtualNetworkResourceId": "[parameters('virtualNetworkResourceId')]" - } - ] - }, - "tags": { - "value": "[parameters('tags')]" - } - }, - "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.32.4.45862", - "templateHash": "83178825086050429" }, - "name": "Private DNS Zones", - "description": "This module deploys a Private DNS zone.", - "owner": "Azure/module-maintainers" - }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } - } - } + "privateDnsZone_MX": { + "copy": { + "name": "privateDnsZone_MX", + "count": "[length(coalesce(parameters('mx'), createArray()))]" }, - "nullable": true - }, - "lockType": { - "type": "object", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-MXRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Specify the name of lock." - } + "expressionEvaluationOptions": { + "scope": "inner" }, - "kind": { - "type": "string", - "allowedValues": [ - "CanNotDelete", - "None", - "ReadOnly" - ], - "nullable": true, - "metadata": { - "description": "Optional. Specify the type of lock." - } - } - }, - "nullable": true - }, - "aType": { - "type": "array", - "items": { - "type": "object", - "properties": { + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } + "value": "[coalesce(parameters('mx'), createArray())[copyIndex()].name]" }, "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } + "value": "[tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'metadata')]" + }, + "mxRecords": { + "value": "[tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'mxRecords')]" }, "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } + "value": "[coalesce(tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'ttl'), 3600)]" }, "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } + "value": "[tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'roleAssignments')]" + } + }, + "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.32.4.45862", + "templateHash": "2520323624213076361" + }, + "name": "Private DNS Zone MX record", + "description": "This module deploys a Private DNS Zone MX record.", + "owner": "Azure/module-maintainers" }, - "aRecords": { - "type": "array", - "items": { - "type": "object", - "properties": { - "ipv4Address": { - "type": "string", - "metadata": { - "description": "Required. The IPv4 address of this A record." + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "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." + } } } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the MX record." } }, - "nullable": true, - "metadata": { - "description": "Optional. The list of A records in the record set." - } - } - } - }, - "nullable": true - }, - "aaaaType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } - }, - "ttl": { - "type": "int", - "nullable": true, "metadata": { - "description": "Optional. The TTL of the record." + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "mxRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of MX records in the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } } }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." + "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')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, - "aaaaRecords": { - "type": "array", - "items": { - "type": "object", + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "MX": { + "type": "Microsoft.Network/privateDnsZones/MX", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", "properties": { - "ipv6Address": { - "type": "string", - "metadata": { - "description": "Required. The IPv6 address of this AAAA record." - } - } + "metadata": "[parameters('metadata')]", + "mxRecords": "[parameters('mxRecords')]", + "ttl": "[parameters('ttl')]" } }, - "nullable": true, - "metadata": { - "description": "Optional. The list of AAAA records in the record set." + "MX_roleAssignments": { + "copy": { + "name": "MX_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/MX/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/MX', parameters('privateDnsZoneName'), 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": [ + "MX" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed MX record." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed MX record." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/MX', parameters('privateDnsZoneName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed MX record." + }, + "value": "[resourceGroup().name]" } } } }, - "nullable": true + "dependsOn": [ + "privateDnsZone" + ] }, - "cnameType": { - "type": "array", - "items": { - "type": "object", - "properties": { + "privateDnsZone_PTR": { + "copy": { + "name": "privateDnsZone_PTR", + "count": "[length(coalesce(parameters('ptr'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-PTRRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } + "value": "[coalesce(parameters('ptr'), createArray())[copyIndex()].name]" }, "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } + "value": "[tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'metadata')]" + }, + "ptrRecords": { + "value": "[tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'ptrRecords')]" }, "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } + "value": "[coalesce(tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'ttl'), 3600)]" }, "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "cnameRecord": { - "type": "object", - "properties": { - "cname": { - "type": "string", - "metadata": { - "description": "Required. The canonical name of the CNAME record." - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. The CNAME record in the record set." - } + "value": "[tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'roleAssignments')]" } - } - }, - "nullable": true - }, - "mxType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } - }, + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." + "_generator": { + "name": "bicep", + "version": "0.32.4.45862", + "templateHash": "3080404733048745471" + }, + "name": "Private DNS Zone PTR record", + "description": "This module deploys a Private DNS Zone PTR record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "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." + } + } + } + }, + "nullable": true } }, - "ttl": { - "type": "int", - "nullable": true, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the PTR record." + } + }, "metadata": { - "description": "Optional. The TTL of the record." + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "ptrRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of PTR records in the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } } }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." + "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')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, - "mxRecords": { - "type": "array", - "items": { - "type": "object", + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "PTR": { + "type": "Microsoft.Network/privateDnsZones/PTR", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", "properties": { - "exchange": { - "type": "string", - "metadata": { - "description": "Required. The domain name of the mail host for this MX record." - } - }, - "preference": { - "type": "int", - "metadata": { - "description": "Required. The preference value for this MX record." - } - } + "metadata": "[parameters('metadata')]", + "ptrRecords": "[parameters('ptrRecords')]", + "ttl": "[parameters('ttl')]" } }, - "nullable": true, - "metadata": { - "description": "Optional. The list of MX records in the record set." + "PTR_roleAssignments": { + "copy": { + "name": "PTR_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/PTR/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/PTR', parameters('privateDnsZoneName'), 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": [ + "PTR" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed PTR record." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed PTR record." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/PTR', parameters('privateDnsZoneName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed PTR record." + }, + "value": "[resourceGroup().name]" } } } }, - "nullable": true + "dependsOn": [ + "privateDnsZone" + ] }, - "ptrType": { - "type": "array", - "items": { - "type": "object", - "properties": { + "privateDnsZone_SOA": { + "copy": { + "name": "privateDnsZone_SOA", + "count": "[length(coalesce(parameters('soa'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-SOARecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } + "value": "[coalesce(parameters('soa'), createArray())[copyIndex()].name]" }, "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } + "value": "[tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'metadata')]" + }, + "soaRecord": { + "value": "[tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'soaRecord')]" }, "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } + "value": "[coalesce(tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'ttl'), 3600)]" }, "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "ptrRecords": { - "type": "array", - "items": { - "type": "object", - "properties": { - "ptrdname": { - "type": "string", - "metadata": { - "description": "Required. The PTR target domain name for this PTR record." - } - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. The list of PTR records in the record set." - } - } - } - }, - "nullable": true - }, - "soaType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } - }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "soaRecord": { - "type": "object", - "properties": { - "email": { - "type": "string", - "metadata": { - "description": "Required. The email contact for this SOA record." - } - }, - "expireTime": { - "type": "int", - "metadata": { - "description": "Required. The expire time for this SOA record." - } - }, - "host": { - "type": "string", - "metadata": { - "description": "Required. The domain name of the authoritative name server for this SOA record." - } - }, - "minimumTtl": { - "type": "int", - "metadata": { - "description": "Required. The minimum value for this SOA record. By convention this is used to determine the negative caching duration." - } - }, - "refreshTime": { - "type": "int", - "metadata": { - "description": "Required. The refresh value for this SOA record." - } - }, - "retryTime": { - "type": "int", - "metadata": { - "description": "Required. The retry time for this SOA record." - } - }, - "serialNumber": { - "type": "int", - "metadata": { - "description": "Required. The serial number for this SOA record." - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. The SOA record in the record set." - } - } - } - }, - "nullable": true - }, - "srvType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } - }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "srvRecords": { - "type": "array", - "items": { - "type": "object", - "properties": { - "priority": { - "type": "int", - "metadata": { - "description": "Required. The priority value for this SRV record." - } - }, - "weight": { - "type": "int", - "metadata": { - "description": "Required. The weight value for this SRV record." - } - }, - "port": { - "type": "int", - "metadata": { - "description": "Required. The port value for this SRV record." - } - }, - "target": { - "type": "string", - "metadata": { - "description": "Required. The target domain name for this SRV record." - } - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. The list of SRV records in the record set." - } - } - } - }, - "nullable": true - }, - "txtType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } - }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "txtRecords": { - "type": "array", - "items": { - "type": "object", - "properties": { - "value": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. The text value of this TXT record." - } - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. The list of TXT records in the record set." - } - } - } - }, - "nullable": true - }, - "virtualNetworkLinkType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "minLength": 1, - "maxLength": 80, - "metadata": { - "description": "Optional. The resource name." - } - }, - "virtualNetworkResourceId": { - "type": "string", - "metadata": { - "description": "Required. The resource ID of the virtual network to link." - } - }, - "location": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The Azure Region where the resource lives." - } - }, - "registrationEnabled": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Is auto-registration of virtual machine records in the virtual network in the Private DNS zone enabled?." - } - }, - "tags": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Resource tags." - } - }, - "resolutionPolicy": { - "type": "string", - "allowedValues": [ - "Default", - "NxDomainRedirect" - ], - "nullable": true, - "metadata": { - "description": "Optional. The resolution type of the private-dns-zone fallback machanism." - } - } - } - }, - "nullable": true - } - }, - "parameters": { - "name": { - "type": "string", - "metadata": { - "description": "Required. Private DNS zone name." - } - }, - "a": { - "$ref": "#/definitions/aType", - "metadata": { - "description": "Optional. Array of A records." - } - }, - "aaaa": { - "$ref": "#/definitions/aaaaType", - "metadata": { - "description": "Optional. Array of AAAA records." - } - }, - "cname": { - "$ref": "#/definitions/cnameType", - "metadata": { - "description": "Optional. Array of CNAME records." - } - }, - "mx": { - "$ref": "#/definitions/mxType", - "metadata": { - "description": "Optional. Array of MX records." - } - }, - "ptr": { - "$ref": "#/definitions/ptrType", - "metadata": { - "description": "Optional. Array of PTR records." - } - }, - "soa": { - "$ref": "#/definitions/soaType", - "metadata": { - "description": "Optional. Array of SOA records." - } - }, - "srv": { - "$ref": "#/definitions/srvType", - "metadata": { - "description": "Optional. Array of SRV records." - } - }, - "txt": { - "$ref": "#/definitions/txtType", - "metadata": { - "description": "Optional. Array of TXT records." - } - }, - "virtualNetworkLinks": { - "$ref": "#/definitions/virtualNetworkLinkType", - "metadata": { - "description": "Optional. Array of custom objects describing vNet links of the DNS zone. Each object should contain properties 'virtualNetworkResourceId' and 'registrationEnabled'. The 'vnetResourceId' is a resource ID of a vNet to link, 'registrationEnabled' (bool) enables automatic DNS registration in the zone for the linked vNet." - } - }, - "location": { - "type": "string", - "defaultValue": "global", - "metadata": { - "description": "Optional. The location of the PrivateDNSZone. Should be global." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "tags": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Tags of the resource." - } - }, - "lock": { - "$ref": "#/definitions/lockType", - "metadata": { - "description": "Optional. The lock settings of the service." - } - }, - "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')]", - "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": "2024-03-01", - "name": "[format('46d3xbcp.res.network-privatednszone.{0}.{1}', replace('0.7.0', '.', '-'), 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" - } - } - } - } - }, - "privateDnsZone": { - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('name')]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]" - }, - "privateDnsZone_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/privateDnsZones/{0}', parameters('name'))]", - "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", - "properties": { - "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", - "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" - }, - "dependsOn": [ - "privateDnsZone" - ] - }, - "privateDnsZone_roleAssignments": { - "copy": { - "name": "privateDnsZone_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}', parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones', 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": [ - "privateDnsZone" - ] - }, - "privateDnsZone_A": { - "copy": { - "name": "privateDnsZone_A", - "count": "[length(coalesce(parameters('a'), createArray()))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-ARecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "privateDnsZoneName": { - "value": "[parameters('name')]" - }, - "name": { - "value": "[coalesce(parameters('a'), createArray())[copyIndex()].name]" - }, - "aRecords": { - "value": "[tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'aRecords')]" - }, - "metadata": { - "value": "[tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'metadata')]" - }, - "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'ttl'), 3600)]" - }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'roleAssignments')]" + "value": "[tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'roleAssignments')]" } }, "template": { @@ -22224,10 +19324,10 @@ "_generator": { "name": "bicep", "version": "0.32.4.45862", - "templateHash": "2531120132215940282" + "templateHash": "6653951445614700931" }, - "name": "Private DNS Zone A record", - "description": "This module deploys a Private DNS Zone A record.", + "name": "Private DNS Zone SOA record", + "description": "This module deploys a Private DNS Zone SOA record.", "owner": "Azure/module-maintainers" }, "definitions": { @@ -22315,21 +19415,21 @@ "name": { "type": "string", "metadata": { - "description": "Required. The name of the A record." + "description": "Required. The name of the SOA record." } }, - "aRecords": { - "type": "array", + "metadata": { + "type": "object", "nullable": true, "metadata": { - "description": "Optional. The list of A records in the record set." + "description": "Optional. The metadata attached to the record set." } }, - "metadata": { + "soaRecord": { "type": "object", "nullable": true, "metadata": { - "description": "Optional. The metadata attached to the record set." + "description": "Optional. A SOA record." } }, "ttl": { @@ -22371,25 +19471,25 @@ "apiVersion": "2020-06-01", "name": "[parameters('privateDnsZoneName')]" }, - "A": { - "type": "Microsoft.Network/privateDnsZones/A", + "SOA": { + "type": "Microsoft.Network/privateDnsZones/SOA", "apiVersion": "2020-06-01", "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", "properties": { - "aRecords": "[parameters('aRecords')]", "metadata": "[parameters('metadata')]", + "soaRecord": "[parameters('soaRecord')]", "ttl": "[parameters('ttl')]" } }, - "A_roleAssignments": { + "SOA_roleAssignments": { "copy": { - "name": "A_roleAssignments", + "name": "SOA_roleAssignments", "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/A/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/A', parameters('privateDnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/SOA/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/SOA', parameters('privateDnsZoneName'), 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]", @@ -22400,7 +19500,7 @@ "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" }, "dependsOn": [ - "A" + "SOA" ] } }, @@ -22408,21 +19508,21 @@ "name": { "type": "string", "metadata": { - "description": "The name of the deployed A record." + "description": "The name of the deployed SOA record." }, "value": "[parameters('name')]" }, "resourceId": { "type": "string", "metadata": { - "description": "The resource ID of the deployed A record." + "description": "The resource ID of the deployed SOA record." }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/A', parameters('privateDnsZoneName'), parameters('name'))]" + "value": "[resourceId('Microsoft.Network/privateDnsZones/SOA', parameters('privateDnsZoneName'), parameters('name'))]" }, "resourceGroupName": { "type": "string", "metadata": { - "description": "The resource group of the deployed A record." + "description": "The resource group of the deployed SOA record." }, "value": "[resourceGroup().name]" } @@ -22433,14 +19533,14 @@ "privateDnsZone" ] }, - "privateDnsZone_AAAA": { + "privateDnsZone_SRV": { "copy": { - "name": "privateDnsZone_AAAA", - "count": "[length(coalesce(parameters('aaaa'), createArray()))]" + "name": "privateDnsZone_SRV", + "count": "[length(coalesce(parameters('srv'), createArray()))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-AAAARecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "name": "[format('{0}-PrivateDnsZone-SRVRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -22451,19 +19551,19 @@ "value": "[parameters('name')]" }, "name": { - "value": "[coalesce(parameters('aaaa'), createArray())[copyIndex()].name]" - }, - "aaaaRecords": { - "value": "[tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'aaaaRecords')]" + "value": "[coalesce(parameters('srv'), createArray())[copyIndex()].name]" }, "metadata": { - "value": "[tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'metadata')]" + "value": "[tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'metadata')]" + }, + "srvRecords": { + "value": "[tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'srvRecords')]" }, "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'ttl'), 3600)]" + "value": "[coalesce(tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'ttl'), 3600)]" }, "roleAssignments": { - "value": "[tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'roleAssignments')]" + "value": "[tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'roleAssignments')]" } }, "template": { @@ -22474,10 +19574,10 @@ "_generator": { "name": "bicep", "version": "0.32.4.45862", - "templateHash": "16709340450244912125" + "templateHash": "5790774778713328446" }, - "name": "Private DNS Zone AAAA record", - "description": "This module deploys a Private DNS Zone AAAA record.", + "name": "Private DNS Zone SRV record", + "description": "This module deploys a Private DNS Zone SRV record.", "owner": "Azure/module-maintainers" }, "definitions": { @@ -22565,21 +19665,21 @@ "name": { "type": "string", "metadata": { - "description": "Required. The name of the AAAA record." + "description": "Required. The name of the SRV record." } }, - "aaaaRecords": { - "type": "array", + "metadata": { + "type": "object", "nullable": true, "metadata": { - "description": "Optional. The list of AAAA records in the record set." + "description": "Optional. The metadata attached to the record set." } }, - "metadata": { - "type": "object", + "srvRecords": { + "type": "array", "nullable": true, "metadata": { - "description": "Optional. The metadata attached to the record set." + "description": "Optional. The list of SRV records in the record set." } }, "ttl": { @@ -22621,25 +19721,25 @@ "apiVersion": "2020-06-01", "name": "[parameters('privateDnsZoneName')]" }, - "AAAA": { - "type": "Microsoft.Network/privateDnsZones/AAAA", + "SRV": { + "type": "Microsoft.Network/privateDnsZones/SRV", "apiVersion": "2020-06-01", "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", "properties": { - "aaaaRecords": "[parameters('aaaaRecords')]", "metadata": "[parameters('metadata')]", + "srvRecords": "[parameters('srvRecords')]", "ttl": "[parameters('ttl')]" } }, - "AAAA_roleAssignments": { + "SRV_roleAssignments": { "copy": { - "name": "AAAA_roleAssignments", + "name": "SRV_roleAssignments", "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/AAAA/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/AAAA', parameters('privateDnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/SRV/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/SRV', parameters('privateDnsZoneName'), 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]", @@ -22650,7 +19750,7 @@ "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" }, "dependsOn": [ - "AAAA" + "SRV" ] } }, @@ -22658,21 +19758,21 @@ "name": { "type": "string", "metadata": { - "description": "The name of the deployed AAAA record." + "description": "The name of the deployed SRV record." }, "value": "[parameters('name')]" }, "resourceId": { "type": "string", "metadata": { - "description": "The resource ID of the deployed AAAA record." + "description": "The resource ID of the deployed SRV record." }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/AAAA', parameters('privateDnsZoneName'), parameters('name'))]" + "value": "[resourceId('Microsoft.Network/privateDnsZones/SRV', parameters('privateDnsZoneName'), parameters('name'))]" }, "resourceGroupName": { "type": "string", "metadata": { - "description": "The resource group of the deployed AAAA record." + "description": "The resource group of the deployed SRV record." }, "value": "[resourceGroup().name]" } @@ -22683,14 +19783,14 @@ "privateDnsZone" ] }, - "privateDnsZone_CNAME": { + "privateDnsZone_TXT": { "copy": { - "name": "privateDnsZone_CNAME", - "count": "[length(coalesce(parameters('cname'), createArray()))]" + "name": "privateDnsZone_TXT", + "count": "[length(coalesce(parameters('txt'), createArray()))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-CNAMERecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "name": "[format('{0}-PrivateDnsZone-TXTRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -22701,19 +19801,19 @@ "value": "[parameters('name')]" }, "name": { - "value": "[coalesce(parameters('cname'), createArray())[copyIndex()].name]" - }, - "cnameRecord": { - "value": "[tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'cnameRecord')]" + "value": "[coalesce(parameters('txt'), createArray())[copyIndex()].name]" }, "metadata": { - "value": "[tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'metadata')]" + "value": "[tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'metadata')]" + }, + "txtRecords": { + "value": "[tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'txtRecords')]" }, "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'ttl'), 3600)]" + "value": "[coalesce(tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'ttl'), 3600)]" }, "roleAssignments": { - "value": "[tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'roleAssignments')]" + "value": "[tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'roleAssignments')]" } }, "template": { @@ -22724,10 +19824,10 @@ "_generator": { "name": "bicep", "version": "0.32.4.45862", - "templateHash": "9976020649752073181" + "templateHash": "1855369119498044639" }, - "name": "Private DNS Zone CNAME record", - "description": "This module deploys a Private DNS Zone CNAME record.", + "name": "Private DNS Zone TXT record", + "description": "This module deploys a Private DNS Zone TXT record.", "owner": "Azure/module-maintainers" }, "definitions": { @@ -22815,14 +19915,7 @@ "name": { "type": "string", "metadata": { - "description": "Required. The name of the CNAME record." - } - }, - "cnameRecord": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. A CNAME record." + "description": "Required. The name of the TXT record." } }, "metadata": { @@ -22839,6 +19932,13 @@ "description": "Optional. The TTL (time-to-live) of the records in the record set." } }, + "txtRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of TXT records in the record set." + } + }, "roleAssignments": { "$ref": "#/definitions/roleAssignmentType", "metadata": { @@ -22871,25 +19971,25 @@ "apiVersion": "2020-06-01", "name": "[parameters('privateDnsZoneName')]" }, - "CNAME": { - "type": "Microsoft.Network/privateDnsZones/CNAME", + "TXT": { + "type": "Microsoft.Network/privateDnsZones/TXT", "apiVersion": "2020-06-01", "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", "properties": { - "cnameRecord": "[parameters('cnameRecord')]", "metadata": "[parameters('metadata')]", - "ttl": "[parameters('ttl')]" + "ttl": "[parameters('ttl')]", + "txtRecords": "[parameters('txtRecords')]" } }, - "CNAME_roleAssignments": { + "TXT_roleAssignments": { "copy": { - "name": "CNAME_roleAssignments", + "name": "TXT_roleAssignments", "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/CNAME/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/CNAME', parameters('privateDnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/TXT/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/TXT', parameters('privateDnsZoneName'), 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]", @@ -22900,7 +20000,7 @@ "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" }, "dependsOn": [ - "CNAME" + "TXT" ] } }, @@ -22908,21 +20008,21 @@ "name": { "type": "string", "metadata": { - "description": "The name of the deployed CNAME record." + "description": "The name of the deployed TXT record." }, "value": "[parameters('name')]" }, "resourceId": { "type": "string", "metadata": { - "description": "The resource ID of the deployed CNAME record." + "description": "The resource ID of the deployed TXT record." }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/CNAME', parameters('privateDnsZoneName'), parameters('name'))]" + "value": "[resourceId('Microsoft.Network/privateDnsZones/TXT', parameters('privateDnsZoneName'), parameters('name'))]" }, "resourceGroupName": { "type": "string", "metadata": { - "description": "The resource group of the deployed CNAME record." + "description": "The resource group of the deployed TXT record." }, "value": "[resourceGroup().name]" } @@ -22933,14 +20033,14 @@ "privateDnsZone" ] }, - "privateDnsZone_MX": { + "privateDnsZone_virtualNetworkLinks": { "copy": { - "name": "privateDnsZone_MX", - "count": "[length(coalesce(parameters('mx'), createArray()))]" + "name": "privateDnsZone_virtualNetworkLinks", + "count": "[length(coalesce(parameters('virtualNetworkLinks'), createArray()))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-MXRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "name": "[format('{0}-PrivateDnsZone-VirtualNetworkLink-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -22951,19 +20051,22 @@ "value": "[parameters('name')]" }, "name": { - "value": "[coalesce(parameters('mx'), createArray())[copyIndex()].name]" + "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'name'), format('{0}-vnetlink', last(split(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()].virtualNetworkResourceId, '/'))))]" }, - "metadata": { - "value": "[tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'metadata')]" + "virtualNetworkResourceId": { + "value": "[coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()].virtualNetworkResourceId]" }, - "mxRecords": { - "value": "[tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'mxRecords')]" + "location": { + "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'location'), 'global')]" }, - "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'ttl'), 3600)]" + "registrationEnabled": { + "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'registrationEnabled'), false())]" }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'roleAssignments')]" + "tags": { + "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + }, + "resolutionPolicy": { + "value": "[tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'resolutionPolicy')]" } }, "template": { @@ -22974,87 +20077,12 @@ "_generator": { "name": "bicep", "version": "0.32.4.45862", - "templateHash": "2520323624213076361" + "templateHash": "15326596012552051215" }, - "name": "Private DNS Zone MX record", - "description": "This module deploys a Private DNS Zone MX record.", + "name": "Private DNS Zone Virtual Network Link", + "description": "This module deploys a Private DNS Zone Virtual Network Link.", "owner": "Azure/module-maintainers" }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } - } - } - }, - "nullable": true - } - }, "parameters": { "privateDnsZoneName": { "type": "string", @@ -23064,54 +20092,44 @@ }, "name": { "type": "string", + "defaultValue": "[format('{0}-vnetlink', last(split(parameters('virtualNetworkResourceId'), '/')))]", "metadata": { - "description": "Required. The name of the MX record." + "description": "Optional. The name of the virtual network link." } }, - "metadata": { - "type": "object", - "nullable": true, + "location": { + "type": "string", + "defaultValue": "global", "metadata": { - "description": "Optional. The metadata attached to the record set." + "description": "Optional. The location of the PrivateDNSZone. Should be global." } }, - "mxRecords": { - "type": "array", + "tags": { + "type": "object", "nullable": true, "metadata": { - "description": "Optional. The list of MX records in the record set." + "description": "Optional. Tags of the resource." } }, - "ttl": { - "type": "int", - "defaultValue": 3600, + "registrationEnabled": { + "type": "bool", + "defaultValue": false, "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." + "description": "Optional. Is auto-registration of virtual machine records in the virtual network in the Private DNS zone enabled?." } }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", + "virtualNetworkResourceId": { + "type": "string", "metadata": { - "description": "Optional. Array of role assignments to create." + "description": "Required. Link to another virtual network resource ID." } - } - }, - "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)))))]" + }, + "resolutionPolicy": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resolution policy on the virtual network link. Only applicable for virtual network links to privatelink zones, and for A,AAAA,CNAME queries. When set to `NxDomainRedirect`, Azure DNS resolver falls back to public resolution if private dns query resolution results in non-existent domain response. `Default` is configured as the default option." } - ], - "builtInRoleNames": { - "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", - "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, "resources": { @@ -23121,60 +20139,49 @@ "apiVersion": "2020-06-01", "name": "[parameters('privateDnsZoneName')]" }, - "MX": { - "type": "Microsoft.Network/privateDnsZones/MX", - "apiVersion": "2020-06-01", + "virtualNetworkLink": { + "type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks", + "apiVersion": "2024-06-01", "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", "properties": { - "metadata": "[parameters('metadata')]", - "mxRecords": "[parameters('mxRecords')]", - "ttl": "[parameters('ttl')]" + "registrationEnabled": "[parameters('registrationEnabled')]", + "virtualNetwork": { + "id": "[parameters('virtualNetworkResourceId')]" + }, + "resolutionPolicy": "[parameters('resolutionPolicy')]" } - }, - "MX_roleAssignments": { - "copy": { - "name": "MX_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/MX/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/MX', parameters('privateDnsZoneName'), 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": [ - "MX" - ] } }, "outputs": { "name": { "type": "string", "metadata": { - "description": "The name of the deployed MX record." + "description": "The name of the deployed virtual network link." }, "value": "[parameters('name')]" }, "resourceId": { "type": "string", "metadata": { - "description": "The resource ID of the deployed MX record." + "description": "The resource ID of the deployed virtual network link." }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/MX', parameters('privateDnsZoneName'), parameters('name'))]" + "value": "[resourceId('Microsoft.Network/privateDnsZones/virtualNetworkLinks', parameters('privateDnsZoneName'), parameters('name'))]" }, "resourceGroupName": { "type": "string", "metadata": { - "description": "The resource group of the deployed MX record." + "description": "The resource group of the deployed virtual network link." }, "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('virtualNetworkLink', '2024-06-01', 'full').location]" } } } @@ -23182,1663 +20189,555 @@ "dependsOn": [ "privateDnsZone" ] + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the private DNS zone was deployed into." + }, + "value": "[resourceGroup().name]" }, - "privateDnsZone_PTR": { - "copy": { - "name": "privateDnsZone_PTR", - "count": "[length(coalesce(parameters('ptr'), createArray()))]" + "name": { + "type": "string", + "metadata": { + "description": "The name of the private DNS zone." }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-PTRRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the private DNS zone." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones', parameters('name'))]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('privateDnsZone', '2020-06-01', 'full').location]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('{0}-container-registry-deployment', variables('nameFormatted')), 64)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[variables('nameFormatted')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "acrSku": { + "value": "Premium" + }, + "acrAdminUserEnabled": { + "value": false + }, + "anonymousPullEnabled": { + "value": false + }, + "dataEndpointEnabled": { + "value": false + }, + "networkRuleBypassOptions": { + "value": "AzureServices" + }, + "networkRuleSetDefaultAction": "[if(parameters('networkIsolation'), createObject('value', 'Deny'), createObject('value', 'Allow'))]", + "exportPolicyStatus": "[if(parameters('networkIsolation'), createObject('value', 'disabled'), createObject('value', 'enabled'))]", + "publicNetworkAccess": "[if(parameters('networkIsolation'), createObject('value', 'Disabled'), createObject('value', 'Enabled'))]", + "zoneRedundancy": { + "value": "Disabled" + }, + "managedIdentities": { + "value": { + "systemAssigned": true + } + }, + "diagnosticSettings": { + "value": [ + { + "workspaceResourceId": "[parameters('logAnalyticsWorkspaceResourceId')]" + } + ] + }, + "privateEndpoints": "[if(parameters('networkIsolation'), createObject('value', createArray(createObject('privateDnsZoneGroup', createObject('privateDnsZoneGroupConfigs', createArray(createObject('privateDnsZoneResourceId', reference(resourceId('Microsoft.Resources/deployments', 'private-dns-acr-deployment'), '2022-09-01').outputs.resourceId.value))), 'subnetResourceId', parameters('virtualNetworkSubnetResourceId')))), createObject('value', createArray()))]" + }, + "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.33.93.31351", + "templateHash": "350214817154408151" + }, + "name": "Azure Container Registries (ACR)", + "description": "This module deploys an Azure Container Registry (ACR)." + }, + "definitions": { + "privateEndpointOutputType": { + "type": "object", "properties": { - "expressionEvaluationOptions": { - "scope": "inner" + "name": { + "type": "string", + "metadata": { + "description": "The name of the private endpoint." + } }, - "mode": "Incremental", - "parameters": { - "privateDnsZoneName": { - "value": "[parameters('name')]" - }, - "name": { - "value": "[coalesce(parameters('ptr'), createArray())[copyIndex()].name]" - }, + "resourceId": { + "type": "string", "metadata": { - "value": "[tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'metadata')]" - }, - "ptrRecords": { - "value": "[tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'ptrRecords')]" - }, - "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'ttl'), 3600)]" - }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'roleAssignments')]" + "description": "The resource ID of the private endpoint." } }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "languageVersion": "2.0", - "contentVersion": "1.0.0.0", + "groupId": { + "type": "string", + "nullable": true, "metadata": { - "_generator": { - "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "3080404733048745471" - }, - "name": "Private DNS Zone PTR record", - "description": "This module deploys a Private DNS Zone PTR record.", - "owner": "Azure/module-maintainers" - }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } - } + "description": "The group Id for the private endpoint Group." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "FQDN that resolves to private endpoint IP address." } }, - "nullable": true - } - }, - "parameters": { - "privateDnsZoneName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the PTR record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata attached to the record set." - } - }, - "ptrRecords": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. The list of PTR records in the record set." - } - }, - "ttl": { - "type": "int", - "defaultValue": 3600, - "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - } - }, - "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)))))]" + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "A list of private IP addresses of the private endpoint." + } } - ], - "builtInRoleNames": { - "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", - "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, - "resources": { - "privateDnsZone": { - "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" - }, - "PTR": { - "type": "Microsoft.Network/privateDnsZones/PTR", - "apiVersion": "2020-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "properties": { - "metadata": "[parameters('metadata')]", - "ptrRecords": "[parameters('ptrRecords')]", - "ttl": "[parameters('ttl')]" - } - }, - "PTR_roleAssignments": { - "copy": { - "name": "PTR_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/PTR/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/PTR', parameters('privateDnsZoneName'), 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": [ - "PTR" - ] - } + "metadata": { + "description": "The custom DNS configurations of the private endpoint." + } + }, + "networkInterfaceResourceIds": { + "type": "array", + "items": { + "type": "string" }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the deployed PTR record." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the deployed PTR record." - }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/PTR', parameters('privateDnsZoneName'), parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group of the deployed PTR record." - }, - "value": "[resourceGroup().name]" - } + "metadata": { + "description": "The IDs of the network interfaces associated with the private endpoint." } } }, - "dependsOn": [ - "privateDnsZone" - ] + "metadata": { + "__bicep_export!": true + } }, - "privateDnsZone_SOA": { - "copy": { - "name": "privateDnsZone_SOA", - "count": "[length(coalesce(parameters('soa'), createArray()))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-SOARecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "scopeMapsType": { + "type": "object", "properties": { - "expressionEvaluationOptions": { - "scope": "inner" + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the scope map." + } }, - "mode": "Incremental", - "parameters": { - "privateDnsZoneName": { - "value": "[parameters('name')]" - }, - "name": { - "value": "[coalesce(parameters('soa'), createArray())[copyIndex()].name]" + "actions": { + "type": "array", + "items": { + "type": "string" }, "metadata": { - "value": "[tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'metadata')]" - }, - "soaRecord": { - "value": "[tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'soaRecord')]" - }, - "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'ttl'), 3600)]" - }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'roleAssignments')]" + "description": "Required. The list of scoped permissions for registry artifacts." } }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "languageVersion": "2.0", - "contentVersion": "1.0.0.0", + "description": { + "type": "string", + "nullable": true, "metadata": { - "_generator": { - "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "6653951445614700931" - }, - "name": "Private DNS Zone SOA record", - "description": "This module deploys a Private DNS Zone SOA record.", - "owner": "Azure/module-maintainers" - }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } - } - } - }, - "nullable": true - } - }, - "parameters": { - "privateDnsZoneName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the SOA record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata attached to the record set." - } - }, - "soaRecord": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. A SOA record." - } - }, - "ttl": { - "type": "int", - "defaultValue": 3600, - "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - } - }, - "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')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", - "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" - } - }, - "resources": { - "privateDnsZone": { - "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" - }, - "SOA": { - "type": "Microsoft.Network/privateDnsZones/SOA", - "apiVersion": "2020-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "properties": { - "metadata": "[parameters('metadata')]", - "soaRecord": "[parameters('soaRecord')]", - "ttl": "[parameters('ttl')]" - } - }, - "SOA_roleAssignments": { - "copy": { - "name": "SOA_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/SOA/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/SOA', parameters('privateDnsZoneName'), 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": [ - "SOA" - ] - } - }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the deployed SOA record." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the deployed SOA record." - }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/SOA', parameters('privateDnsZoneName'), parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group of the deployed SOA record." - }, - "value": "[resourceGroup().name]" - } + "description": "Optional. The user friendly description of the scope map." } } }, - "dependsOn": [ - "privateDnsZone" - ] + "metadata": { + "__bicep_export!": true, + "description": "The type for a scope map." + } }, - "privateDnsZone_SRV": { - "copy": { - "name": "privateDnsZone_SRV", - "count": "[length(coalesce(parameters('srv'), createArray()))]" + "cacheRuleType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the cache rule. Will be derived from the source repository name if not defined." + } + }, + "sourceRepository": { + "type": "string", + "metadata": { + "description": "Required. Source repository pulled from upstream." + } + }, + "targetRepository": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Target repository specified in docker pull command. E.g.: docker pull myregistry.azurecr.io/{targetRepository}:{tag}." + } + }, + "credentialSetResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the credential store which is associated with the cache rule." + } + } }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-SRVRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "metadata": { + "__bicep_export!": true, + "description": "The type for a cache rule." + } + }, + "credentialSetType": { + "type": "object", "properties": { - "expressionEvaluationOptions": { - "scope": "inner" + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the credential set." + } }, - "mode": "Incremental", - "parameters": { - "privateDnsZoneName": { - "value": "[parameters('name')]" - }, - "name": { - "value": "[coalesce(parameters('srv'), createArray())[copyIndex()].name]" - }, + "managedIdentities": { + "$ref": "#/definitions/managedIdentityOnlySysAssignedType", + "nullable": true, "metadata": { - "value": "[tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'metadata')]" - }, - "srvRecords": { - "value": "[tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'srvRecords')]" - }, - "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'ttl'), 3600)]" + "description": "Optional. The managed identity definition for this resource." + } + }, + "authCredentials": { + "type": "array", + "items": { + "$ref": "#/definitions/authCredentialsType" }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'roleAssignments')]" + "metadata": { + "description": "Required. List of authentication credentials stored for an upstream. Usually consists of a primary and an optional secondary credential." } }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "languageVersion": "2.0", - "contentVersion": "1.0.0.0", + "loginServer": { + "type": "string", "metadata": { - "_generator": { - "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "5790774778713328446" - }, - "name": "Private DNS Zone SRV record", - "description": "This module deploys a Private DNS Zone SRV record.", - "owner": "Azure/module-maintainers" + "description": "Required. The credentials are stored for this upstream or login server." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for a credential set." + } + }, + "replicationType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the replication." + } + }, + "location": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "regionEndpointEnabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Specifies whether the replication regional endpoint is enabled. Requests will not be routed to a replication whose regional endpoint is disabled, however its data will continue to be synced with other replications." + } + }, + "zoneRedundancy": { + "type": "string", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "nullable": true, + "metadata": { + "description": "Optional. Whether or not zone redundancy is enabled for this container registry." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for a replication." + } + }, + "webhookType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "minLength": 5, + "maxLength": 50, + "metadata": { + "description": "Optional. The name of the registry webhook." + } + }, + "serviceUri": { + "type": "string", + "metadata": { + "description": "Required. The service URI for the webhook to post notifications." + } + }, + "status": { + "type": "string", + "allowedValues": [ + "disabled", + "enabled" + ], + "nullable": true, + "metadata": { + "description": "Optional. The status of the webhook at the time the operation was called." + } + }, + "action": { + "type": "array", + "items": { + "type": "string" }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } - } - } - }, - "nullable": true - } + "nullable": true, + "metadata": { + "description": "Optional. The list of actions that trigger the webhook to post notifications." + } + }, + "location": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "customHeaders": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Custom headers that will be added to the webhook notifications." + } + }, + "scope": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The scope of repositories where the event can be triggered. For example, 'foo:*' means events for all tags under repository 'foo'. 'foo:bar' means events for 'foo:bar' only. 'foo' is equivalent to 'foo:latest'. Empty means all events." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for a webhook." + } + }, + "_1.privateEndpointCustomDnsConfigType": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. FQDN that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" }, - "parameters": { - "privateDnsZoneName": { + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "_1.privateEndpointIpConfigurationType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource that is unique within a resource group." + } + }, + "properties": { + "type": "object", + "properties": { + "groupId": { "type": "string", "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." } }, - "name": { + "memberName": { "type": "string", "metadata": { - "description": "Required. The name of the SRV record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata attached to the record set." - } - }, - "srvRecords": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. The list of SRV records in the record set." - } - }, - "ttl": { - "type": "int", - "defaultValue": 3600, - "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to." } }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", + "privateIPAddress": { + "type": "string", "metadata": { - "description": "Optional. Array of role assignments to create." - } - } - }, - "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')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", - "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" - } - }, - "resources": { - "privateDnsZone": { - "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" - }, - "SRV": { - "type": "Microsoft.Network/privateDnsZones/SRV", - "apiVersion": "2020-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "properties": { - "metadata": "[parameters('metadata')]", - "srvRecords": "[parameters('srvRecords')]", - "ttl": "[parameters('ttl')]" + "description": "Required. A private IP address obtained from the private endpoint's subnet." } - }, - "SRV_roleAssignments": { - "copy": { - "name": "SRV_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/SRV/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/SRV', parameters('privateDnsZoneName'), 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": [ - "SRV" - ] } }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the deployed SRV record." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the deployed SRV record." - }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/SRV', parameters('privateDnsZoneName'), parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group of the deployed SRV record." - }, - "value": "[resourceGroup().name]" - } + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." } } }, - "dependsOn": [ - "privateDnsZone" - ] + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } }, - "privateDnsZone_TXT": { - "copy": { - "name": "privateDnsZone_TXT", - "count": "[length(coalesce(parameters('txt'), createArray()))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-TXTRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "_1.privateEndpointPrivateDnsZoneGroupType": { + "type": "object", "properties": { - "expressionEvaluationOptions": { - "scope": "inner" + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } }, - "mode": "Incremental", - "parameters": { - "privateDnsZoneName": { - "value": "[parameters('name')]" - }, - "name": { - "value": "[coalesce(parameters('txt'), createArray())[copyIndex()].name]" + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "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": { - "value": "[tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'metadata')]" - }, - "txtRecords": { - "value": "[tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'txtRecords')]" - }, - "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'ttl'), 3600)]" - }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'roleAssignments')]" - } - }, - "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.32.4.45862", - "templateHash": "1855369119498044639" - }, - "name": "Private DNS Zone TXT record", - "description": "This module deploys a Private DNS Zone TXT record.", - "owner": "Azure/module-maintainers" - }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } - } - } - }, - "nullable": true - } - }, - "parameters": { - "privateDnsZoneName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the TXT record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata attached to the record set." - } - }, - "ttl": { - "type": "int", - "defaultValue": 3600, - "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." - } - }, - "txtRecords": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. The list of TXT records in the record set." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - } - }, - "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')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", - "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" - } - }, - "resources": { - "privateDnsZone": { - "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" - }, - "TXT": { - "type": "Microsoft.Network/privateDnsZones/TXT", - "apiVersion": "2020-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "properties": { - "metadata": "[parameters('metadata')]", - "ttl": "[parameters('ttl')]", - "txtRecords": "[parameters('txtRecords')]" - } - }, - "TXT_roleAssignments": { - "copy": { - "name": "TXT_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/TXT/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/TXT', parameters('privateDnsZoneName'), 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": [ - "TXT" - ] - } - }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the deployed TXT record." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the deployed TXT record." - }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/TXT', parameters('privateDnsZoneName'), parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group of the deployed TXT record." - }, - "value": "[resourceGroup().name]" - } + "description": "Required. The private DNS Zone Groups to associate the Private Endpoint. A DNS Zone Group can support up to 5 DNS zones." } } }, - "dependsOn": [ - "privateDnsZone" - ] + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } }, - "privateDnsZone_virtualNetworkLinks": { - "copy": { - "name": "privateDnsZone_virtualNetworkLinks", - "count": "[length(coalesce(parameters('virtualNetworkLinks'), createArray()))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-VirtualNetworkLink-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "authCredentialsType": { + "type": "object", "properties": { - "expressionEvaluationOptions": { - "scope": "inner" + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the credential." + } }, - "mode": "Incremental", - "parameters": { - "privateDnsZoneName": { - "value": "[parameters('name')]" - }, - "name": { - "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'name'), format('{0}-vnetlink', last(split(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()].virtualNetworkResourceId, '/'))))]" - }, - "virtualNetworkResourceId": { - "value": "[coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()].virtualNetworkResourceId]" - }, - "location": { - "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'location'), 'global')]" - }, - "registrationEnabled": { - "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'registrationEnabled'), false())]" - }, - "tags": { - "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" - }, - "resolutionPolicy": { - "value": "[tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'resolutionPolicy')]" + "usernameSecretIdentifier": { + "type": "string", + "metadata": { + "description": "Required. KeyVault Secret URI for accessing the username." } }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "languageVersion": "2.0", - "contentVersion": "1.0.0.0", + "passwordSecretIdentifier": { + "type": "string", "metadata": { - "_generator": { - "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "15326596012552051215" - }, - "name": "Private DNS Zone Virtual Network Link", - "description": "This module deploys a Private DNS Zone Virtual Network Link.", - "owner": "Azure/module-maintainers" - }, - "parameters": { - "privateDnsZoneName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", - "defaultValue": "[format('{0}-vnetlink', last(split(parameters('virtualNetworkResourceId'), '/')))]", - "metadata": { - "description": "Optional. The name of the virtual network link." - } - }, - "location": { - "type": "string", - "defaultValue": "global", - "metadata": { - "description": "Optional. The location of the PrivateDNSZone. Should be global." - } - }, - "tags": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Tags of the resource." - } - }, - "registrationEnabled": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "Optional. Is auto-registration of virtual machine records in the virtual network in the Private DNS zone enabled?." - } - }, - "virtualNetworkResourceId": { - "type": "string", - "metadata": { - "description": "Required. Link to another virtual network resource ID." - } - }, - "resolutionPolicy": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The resolution policy on the virtual network link. Only applicable for virtual network links to privatelink zones, and for A,AAAA,CNAME queries. When set to `NxDomainRedirect`, Azure DNS resolver falls back to public resolution if private dns query resolution results in non-existent domain response. `Default` is configured as the default option." - } - } - }, - "resources": { - "privateDnsZone": { - "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" - }, - "virtualNetworkLink": { - "type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks", - "apiVersion": "2024-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", - "properties": { - "registrationEnabled": "[parameters('registrationEnabled')]", - "virtualNetwork": { - "id": "[parameters('virtualNetworkResourceId')]" - }, - "resolutionPolicy": "[parameters('resolutionPolicy')]" - } - } - }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the deployed virtual network link." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the deployed virtual network link." - }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/virtualNetworkLinks', parameters('privateDnsZoneName'), parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group of the deployed virtual network link." - }, - "value": "[resourceGroup().name]" - }, - "location": { - "type": "string", - "metadata": { - "description": "The location the resource was deployed into." - }, - "value": "[reference('virtualNetworkLink', '2024-06-01', 'full').location]" - } + "description": "Required. KeyVault Secret URI for accessing the password." } } }, - "dependsOn": [ - "privateDnsZone" - ] - } - }, - "outputs": { - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group the private DNS zone was deployed into." - }, - "value": "[resourceGroup().name]" - }, - "name": { - "type": "string", - "metadata": { - "description": "The name of the private DNS zone." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the private DNS zone." - }, - "value": "[resourceId('Microsoft.Network/privateDnsZones', parameters('name'))]" - }, - "location": { - "type": "string", "metadata": { - "description": "The location the resource was deployed into." - }, - "value": "[reference('privateDnsZone', '2020-06-01', 'full').location]" - } - } - } - } - }, - "storageAccount": { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[take(format('{0}-storage-account-deployment', variables('nameFormatted')), 64)]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[variables('nameFormatted')]" - }, - "location": { - "value": "[parameters('location')]" - }, - "tags": { - "value": "[parameters('tags')]" - }, - "publicNetworkAccess": "[if(parameters('networkIsolation'), createObject('value', 'Disabled'), createObject('value', 'Enabled'))]", - "accessTier": { - "value": "Hot" - }, - "allowBlobPublicAccess": { - "value": false - }, - "allowSharedKeyAccess": { - "value": false - }, - "allowCrossTenantReplication": { - "value": false - }, - "minimumTlsVersion": { - "value": "TLS1_2" - }, - "networkAcls": { - "value": { - "defaultAction": "Allow" - } - }, - "supportsHttpsTrafficOnly": { - "value": true - }, - "diagnosticSettings": { - "value": [ - { - "workspaceResourceId": "[parameters('logAnalyticsWorkspaceResourceId')]" + "description": "The type for auth credentials.", + "__bicep_imported_from!": { + "sourceTemplate": "credential-set/main.bicep" + } } - ] - }, - "privateEndpoints": "[if(parameters('networkIsolation'), createObject('value', createArray(createObject('privateDnsZoneGroup', createObject('privateDnsZoneGroupConfigs', createArray(createObject('privateDnsZoneResourceId', reference('blobPrivateDnsZone').outputs.resourceId.value))), 'service', 'blob', 'subnetResourceId', parameters('virtualNetworkSubnetResourceId')), createObject('privateDnsZoneGroup', createObject('privateDnsZoneGroupConfigs', createArray(createObject('privateDnsZoneResourceId', reference('filePrivateDnsZone').outputs.resourceId.value))), 'service', 'file', 'subnetResourceId', parameters('virtualNetworkSubnetResourceId')))), createObject('value', createArray()))]", - "roleAssignments": { - "value": "[parameters('roleAssignments')]" - } - }, - "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.33.13.18514", - "templateHash": "3537078469744044161" }, - "name": "Storage Accounts", - "description": "This module deploys a Storage Account." - }, - "definitions": { - "privateEndpointOutputType": { + "customerManagedKeyWithAutoRotateType": { "type": "object", "properties": { - "name": { + "keyVaultResourceId": { "type": "string", "metadata": { - "description": "The name of the private endpoint." + "description": "Required. The resource ID of a key vault to reference a customer managed key for encryption from." } }, - "resourceId": { + "keyName": { "type": "string", "metadata": { - "description": "The resource ID of the private endpoint." + "description": "Required. The name of the customer managed key to use for encryption." } }, - "groupId": { + "keyVersion": { "type": "string", "nullable": true, "metadata": { - "description": "The group Id for the private endpoint Group." - } - }, - "customDnsConfigs": { - "type": "array", - "items": { - "type": "object", - "properties": { - "fqdn": { - "type": "string", - "nullable": true, - "metadata": { - "description": "FQDN that resolves to private endpoint IP address." - } - }, - "ipAddresses": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "A list of private IP addresses of the private endpoint." - } - } - } - }, - "metadata": { - "description": "The custom DNS configurations of the private endpoint." - } - }, - "networkInterfaceResourceIds": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "The IDs of the network interfaces associated with the private endpoint." - } - } - }, - "metadata": { - "__bicep_export!": true - } - }, - "networkAclsType": { - "type": "object", - "properties": { - "resourceAccessRules": { - "type": "array", - "items": { - "type": "object", - "properties": { - "tenantId": { - "type": "string", - "metadata": { - "description": "Required. The ID of the tenant in which the resource resides in." - } - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "Required. The resource ID of the target service. Can also contain a wildcard, if multiple services e.g. in a resource group should be included." - } - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. Sets the resource access rules. Array entries must consist of \"tenantId\" and \"resourceId\" fields only." - } - }, - "bypass": { - "type": "string", - "allowedValues": [ - "AzureServices", - "AzureServices, Logging", - "AzureServices, Logging, Metrics", - "AzureServices, Metrics", - "Logging", - "Logging, Metrics", - "Metrics", - "None" - ], - "nullable": true, - "metadata": { - "description": "Optional. Specifies whether traffic is bypassed for Logging/Metrics/AzureServices. Possible values are any combination of Logging,Metrics,AzureServices (For example, \"Logging, Metrics\"), or None to bypass none of those traffics." - } - }, - "virtualNetworkRules": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. Sets the virtual network rules." - } - }, - "ipRules": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. Sets the IP ACL rules." - } - }, - "defaultAction": { - "type": "string", - "allowedValues": [ - "Allow", - "Deny" - ], - "nullable": true, - "metadata": { - "description": "Optional. Specifies the default action of allow or deny when no other rules match." - } - } - }, - "metadata": { - "__bicep_export!": true - } - }, - "secretsExportConfigurationType": { - "type": "object", - "properties": { - "keyVaultResourceId": { - "type": "string", - "metadata": { - "description": "Required. The key vault name where to store the keys and connection strings generated by the modules." - } - }, - "accessKey1Name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The accessKey1 secret name to create." - } - }, - "connectionString1Name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The connectionString1 secret name to create." - } - }, - "accessKey2Name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The accessKey2 secret name to create." - } - }, - "connectionString2Name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The connectionString2 secret name to create." - } - } - }, - "metadata": { - "__bicep_export!": true - } - }, - "localUserType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the local user used for SFTP Authentication." - } - }, - "hasSharedKey": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Indicates whether shared key exists. Set it to false to remove existing shared key." - } - }, - "hasSshKey": { - "type": "bool", - "metadata": { - "description": "Required. Indicates whether SSH key exists. Set it to false to remove existing SSH key." - } - }, - "hasSshPassword": { - "type": "bool", - "metadata": { - "description": "Required. Indicates whether SSH password exists. Set it to false to remove existing SSH password." - } - }, - "homeDirectory": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The local user home directory." - } - }, - "permissionScopes": { - "type": "array", - "items": { - "$ref": "#/definitions/permissionScopeType" - }, - "metadata": { - "description": "Required. The permission scopes of the local user." - } - }, - "sshAuthorizedKeys": { - "type": "array", - "items": { - "$ref": "#/definitions/sshAuthorizedKeyType" - }, - "nullable": true, - "metadata": { - "description": "Optional. The local user SSH authorized keys for SFTP." - } - } - }, - "metadata": { - "__bicep_export!": true - } - }, - "_1.privateEndpointCustomDnsConfigType": { - "type": "object", - "properties": { - "fqdn": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. FQDN that resolves to private endpoint IP address." - } - }, - "ipAddresses": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. A list of private IP addresses of the private endpoint." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } - } - }, - "_1.privateEndpointIpConfigurationType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the resource that is unique within a resource group." - } - }, - "properties": { - "type": "object", - "properties": { - "groupId": { - "type": "string", - "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." - } - }, - "memberName": { - "type": "string", - "metadata": { - "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to." - } - }, - "privateIPAddress": { - "type": "string", - "metadata": { - "description": "Required. A private IP address obtained from the private endpoint's subnet." - } - } - }, - "metadata": { - "description": "Required. Properties of private endpoint IP configurations." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } - } - }, - "_1.privateEndpointPrivateDnsZoneGroupType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the Private DNS Zone Group." - } - }, - "privateDnsZoneGroupConfigs": { - "type": "array", - "items": { - "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": "Required. The private DNS Zone Groups to associate the Private Endpoint. A DNS Zone Group can support up to 5 DNS zones." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } - } - }, - "_1.secretSetOutputType": { - "type": "object", - "properties": { - "secretResourceId": { - "type": "string", - "metadata": { - "description": "The resourceId of the exported secret." - } - }, - "secretUri": { - "type": "string", - "metadata": { - "description": "The secret URI of the exported secret." - } - }, - "secretUriWithVersion": { - "type": "string", - "metadata": { - "description": "The secret URI with version of the exported secret." - } - } - }, - "metadata": { - "description": "An AVM-aligned type for the output of the secret set via the secrets export feature.", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } - } - }, - "customerManagedKeyWithAutoRotateType": { - "type": "object", - "properties": { - "keyVaultResourceId": { - "type": "string", - "metadata": { - "description": "Required. The resource ID of a key vault to reference a customer managed key for encryption from." - } - }, - "keyName": { - "type": "string", - "metadata": { - "description": "Required. The name of the customer managed key to use for encryption." - } - }, - "keyVersion": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The version of the customer managed key to reference for encryption. If not provided, using version as per 'autoRotationEnabled' setting." + "description": "Optional. The version of the customer managed key to reference for encryption. If not provided, using version as per 'autoRotationEnabled' setting." } }, "autoRotationEnabled": { @@ -25043,49 +20942,39 @@ } } }, - "permissionScopeType": { + "managedIdentityOnlySysAssignedType": { "type": "object", "properties": { - "permissions": { - "type": "string", - "metadata": { - "description": "Required. The permissions for the local user. Possible values include: Read (r), Write (w), Delete (d), List (l), and Create (c)." - } - }, - "resourceName": { - "type": "string", - "metadata": { - "description": "Required. The name of resource, normally the container name or the file share name, used by the local user." - } - }, - "service": { - "type": "string", + "systemAssigned": { + "type": "bool", + "nullable": true, "metadata": { - "description": "Required. The service used by the local user, e.g. blob, file." + "description": "Optional. Enables system assigned managed identity on the resource." } } }, "metadata": { + "description": "An AVM-aligned type for a managed identity configuration. To be used if only system-assigned identities are supported by the resource provider.", "__bicep_imported_from!": { - "sourceTemplate": "local-user/main.bicep" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, - "privateEndpointMultiServiceType": { + "privateEndpointSingleServiceType": { "type": "object", "properties": { "name": { "type": "string", "nullable": true, "metadata": { - "description": "Optional. The name of the private endpoint." + "description": "Optional. The name of the Private Endpoint." } }, "location": { "type": "string", "nullable": true, "metadata": { - "description": "Optional. The location to deploy the private endpoint to." + "description": "Optional. The location to deploy the Private Endpoint to." } }, "privateLinkServiceConnectionName": { @@ -25097,8 +20986,9 @@ }, "service": { "type": "string", + "nullable": true, "metadata": { - "description": "Required. The subresource to deploy the private endpoint for. For example \"blob\", \"table\", \"queue\" or \"file\" for a Storage Account's Private Endpoints." + "description": "Optional. The subresource to deploy the Private Endpoint for. For example \"vault\" for a Key Vault Private Endpoint." } }, "subnetResourceId": { @@ -25118,7 +21008,7 @@ "$ref": "#/definitions/_1.privateEndpointPrivateDnsZoneGroupType", "nullable": true, "metadata": { - "description": "Optional. The private DNS zone group to configure for the private endpoint." + "description": "Optional. The private DNS Zone Group to configure for the Private Endpoint." } }, "isManualConnection": { @@ -25153,7 +21043,7 @@ }, "nullable": true, "metadata": { - "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." + "description": "Optional. A list of IP configurations of the Private Endpoint. This will be used to map to the first-party Service endpoints." } }, "applicationSecurityGroupResourceIds": { @@ -25163,14 +21053,14 @@ }, "nullable": true, "metadata": { - "description": "Optional. Application security groups in which the private endpoint IP configuration is included." + "description": "Optional. Application security groups in which the Private Endpoint IP configuration is included." } }, "customNetworkInterfaceName": { "type": "string", "nullable": true, "metadata": { - "description": "Optional. The custom name of the network interface attached to the private endpoint." + "description": "Optional. The custom name of the network interface attached to the Private Endpoint." } }, "lock": { @@ -25194,7 +21084,7 @@ "type": "object", "nullable": true, "metadata": { - "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." + "description": "Optional. Tags to be applied on all resources/Resource Groups in this deployment." } }, "enableTelemetry": { @@ -25206,7 +21096,7 @@ } }, "metadata": { - "description": "An AVM-aligned type for a private endpoint. To be used if the private endpoint's default service / groupId can NOT be assumed (i.e., for services that have more than one subresource, like Storage Account with Blob (blob, table, queue, file, ...).", + "description": "An AVM-aligned type for a private endpoint. To be used if the private endpoint's default service / groupId can be assumed (i.e., for services that only have one Private Endpoint type like 'vault' for key vault).", "__bicep_imported_from!": { "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } @@ -25286,53 +21176,22 @@ "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } - }, - "secretsOutputType": { - "type": "object", - "properties": {}, - "additionalProperties": { - "$ref": "#/definitions/_1.secretSetOutputType", - "metadata": { - "description": "An exported secret's references." - } - }, - "metadata": { - "description": "A map of the exported secrets", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } - } - }, - "sshAuthorizedKeyType": { - "type": "object", - "properties": { - "description": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Description used to store the function/usage of the key." - } - }, - "key": { - "type": "securestring", - "metadata": { - "description": "Required. SSH public key base64 encoded. The format should be: '{keyType} {keyData}', e.g. ssh-rsa AAAABBBB." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "local-user/main.bicep" - } - } } }, "parameters": { "name": { "type": "string", - "maxLength": 24, + "minLength": 5, + "maxLength": 50, "metadata": { - "description": "Required. Name of the Storage Account. Must be lower-case." + "description": "Required. Name of your Azure Container Registry." + } + }, + "acrAdminUserEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Enable admin user that have push / pull permission to the registry." } }, "location": { @@ -25352,245 +21211,184 @@ "description": "Optional. Array of role assignments to create." } }, - "managedIdentities": { - "$ref": "#/definitions/managedIdentityAllType", - "nullable": true, - "metadata": { - "description": "Optional. The managed identity definition for this resource." - } - }, - "kind": { + "acrSku": { "type": "string", - "defaultValue": "StorageV2", + "defaultValue": "Premium", "allowedValues": [ - "Storage", - "StorageV2", - "BlobStorage", - "FileStorage", - "BlockBlobStorage" + "Basic", + "Premium", + "Standard" ], "metadata": { - "description": "Optional. Type of Storage Account to create." + "description": "Optional. Tier of your Azure container registry." } }, - "skuName": { + "exportPolicyStatus": { "type": "string", - "defaultValue": "Standard_GRS", + "defaultValue": "disabled", "allowedValues": [ - "Standard_LRS", - "Standard_GRS", - "Standard_RAGRS", - "Standard_ZRS", - "Premium_LRS", - "Premium_ZRS", - "Standard_GZRS", - "Standard_RAGZRS" + "disabled", + "enabled" ], "metadata": { - "description": "Optional. Storage Account Sku Name." + "description": "Optional. The value that indicates whether the export policy is enabled or not." } }, - "accessTier": { + "quarantinePolicyStatus": { "type": "string", - "defaultValue": "Hot", + "defaultValue": "disabled", "allowedValues": [ - "Premium", - "Hot", - "Cool", - "Cold" + "disabled", + "enabled" ], "metadata": { - "description": "Conditional. Required if the Storage Account kind is set to BlobStorage. The access tier is used for billing. The \"Premium\" access tier is the default value for premium block blobs storage account type and it cannot be changed for the premium block blobs storage account type." + "description": "Optional. The value that indicates whether the quarantine policy is enabled or not. Note, requires the 'acrSku' to be 'Premium'." } }, - "largeFileSharesState": { + "trustPolicyStatus": { "type": "string", - "defaultValue": "Disabled", + "defaultValue": "disabled", "allowedValues": [ - "Disabled", - "Enabled" + "disabled", + "enabled" ], "metadata": { - "description": "Optional. Allow large file shares if sets to 'Enabled'. It cannot be disabled once it is enabled. Only supported on locally redundant and zone redundant file shares. It cannot be set on FileStorage storage accounts (storage accounts for premium file shares)." - } - }, - "azureFilesIdentityBasedAuthentication": { - "type": "object", - "defaultValue": {}, - "metadata": { - "description": "Optional. Provides the identity based authentication settings for Azure Files." - } - }, - "defaultToOAuthAuthentication": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "Optional. A boolean flag which indicates whether the default authentication is OAuth or not." + "description": "Optional. The value that indicates whether the trust policy is enabled or not. Note, requires the 'acrSku' to be 'Premium'." } }, - "allowSharedKeyAccess": { - "type": "bool", - "defaultValue": true, + "retentionPolicyStatus": { + "type": "string", + "defaultValue": "enabled", + "allowedValues": [ + "disabled", + "enabled" + ], "metadata": { - "description": "Optional. Indicates whether the storage account permits requests to be authorized with the account access key via Shared Key. If false, then all requests, including shared access signatures, must be authorized with Azure Active Directory (Azure AD). The default value is null, which is equivalent to true." + "description": "Optional. The value that indicates whether the retention policy is enabled or not." } }, - "privateEndpoints": { - "type": "array", - "items": { - "$ref": "#/definitions/privateEndpointMultiServiceType" - }, - "nullable": true, + "retentionPolicyDays": { + "type": "int", + "defaultValue": 15, "metadata": { - "description": "Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible." + "description": "Optional. The number of days to retain an untagged manifest after which it gets purged." } }, - "managementPolicyRules": { - "type": "array", - "nullable": true, + "azureADAuthenticationAsArmPolicyStatus": { + "type": "string", + "defaultValue": "enabled", + "allowedValues": [ + "disabled", + "enabled" + ], "metadata": { - "description": "Optional. The Storage Account ManagementPolicies Rules." + "description": "Optional. The value that indicates whether the policy for using ARM audience token for a container registry is enabled or not. Default is enabled." } }, - "networkAcls": { - "$ref": "#/definitions/networkAclsType", - "nullable": true, + "softDeletePolicyStatus": { + "type": "string", + "defaultValue": "disabled", + "allowedValues": [ + "disabled", + "enabled" + ], "metadata": { - "description": "Optional. Networks ACLs, this value contains IPs to whitelist and/or Subnet information. If in use, bypass needs to be supplied. For security reasons, it is recommended to set the DefaultAction Deny." + "description": "Optional. Soft Delete policy status. Default is disabled." } }, - "requireInfrastructureEncryption": { - "type": "bool", - "defaultValue": true, + "softDeletePolicyDays": { + "type": "int", + "defaultValue": 7, "metadata": { - "description": "Optional. A Boolean indicating whether or not the service applies a secondary layer of encryption with platform managed keys for data at rest. For security reasons, it is recommended to set it to true." + "description": "Optional. The number of days after which a soft-deleted item is permanently deleted." } }, - "allowCrossTenantReplication": { + "dataEndpointEnabled": { "type": "bool", "defaultValue": false, "metadata": { - "description": "Optional. Allow or disallow cross AAD tenant object replication." + "description": "Optional. Enable a single data endpoint per region for serving data. Not relevant in case of disabled public access. Note, requires the 'acrSku' to be 'Premium'." } }, - "customDomainName": { + "publicNetworkAccess": { "type": "string", - "defaultValue": "", - "metadata": { - "description": "Optional. Sets the custom domain name assigned to the storage account. Name is the CNAME source." - } - }, - "customDomainUseSubDomainName": { - "type": "bool", - "defaultValue": false, + "nullable": true, + "allowedValues": [ + "Enabled", + "Disabled" + ], "metadata": { - "description": "Optional. Indicates whether indirect CName validation is enabled. This should only be set on updates." + "description": "Optional. Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set and networkRuleSetIpRules are not set. Note, requires the 'acrSku' to be 'Premium'." } }, - "dnsEndpointType": { + "networkRuleBypassOptions": { "type": "string", - "defaultValue": "", + "defaultValue": "AzureServices", "allowedValues": [ - "", - "AzureDnsZone", - "Standard" + "AzureServices", + "None" ], "metadata": { - "description": "Optional. Allows you to specify the type of endpoint. Set this to AzureDNSZone to create a large number of accounts in a single subscription, which creates accounts in an Azure DNS Zone and the endpoint URL will have an alphanumeric DNS Zone identifier." - } - }, - "blobServices": { - "type": "object", - "defaultValue": "[if(not(equals(parameters('kind'), 'FileStorage')), createObject('containerDeleteRetentionPolicyEnabled', true(), 'containerDeleteRetentionPolicyDays', 7, 'deleteRetentionPolicyEnabled', true(), 'deleteRetentionPolicyDays', 6), createObject())]", - "metadata": { - "description": "Optional. Blob service and containers to deploy." - } - }, - "fileServices": { - "type": "object", - "defaultValue": {}, - "metadata": { - "description": "Optional. File service and shares to deploy." - } - }, - "queueServices": { - "type": "object", - "defaultValue": {}, - "metadata": { - "description": "Optional. Queue service and queues to create." - } - }, - "tableServices": { - "type": "object", - "defaultValue": {}, - "metadata": { - "description": "Optional. Table service and tables to create." - } - }, - "allowBlobPublicAccess": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "Optional. Indicates whether public access is enabled for all blobs or containers in the storage account. For security reasons, it is recommended to set it to false." + "description": "Optional. Whether to allow trusted Azure services to access a network restricted registry." } }, - "minimumTlsVersion": { + "networkRuleSetDefaultAction": { "type": "string", - "defaultValue": "TLS1_2", + "defaultValue": "Deny", "allowedValues": [ - "TLS1_2", - "TLS1_3" + "Allow", + "Deny" ], "metadata": { - "description": "Optional. Set the minimum TLS version on request to storage. The TLS versions 1.0 and 1.1 are deprecated and not supported anymore." - } - }, - "enableHierarchicalNamespace": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "Conditional. If true, enables Hierarchical Namespace for the storage account. Required if enableSftp or enableNfsV3 is set to true." + "description": "Optional. The default action of allow or deny when no other rules match." } }, - "enableSftp": { - "type": "bool", - "defaultValue": false, + "networkRuleSetIpRules": { + "type": "array", + "nullable": true, "metadata": { - "description": "Optional. If true, enables Secure File Transfer Protocol for the storage account. Requires enableHierarchicalNamespace to be true." + "description": "Optional. The IP ACL rules. Note, requires the 'acrSku' to be 'Premium'." } }, - "localUsers": { + "privateEndpoints": { "type": "array", "items": { - "$ref": "#/definitions/localUserType" + "$ref": "#/definitions/privateEndpointSingleServiceType" }, "nullable": true, "metadata": { - "description": "Optional. Local users to deploy for SFTP authentication." + "description": "Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible. Note, requires the 'acrSku' to be 'Premium'." } }, - "isLocalUserEnabled": { - "type": "bool", - "defaultValue": false, + "zoneRedundancy": { + "type": "string", + "defaultValue": "Enabled", + "allowedValues": [ + "Disabled", + "Enabled" + ], "metadata": { - "description": "Optional. Enables local users feature, if set to true." + "description": "Optional. Whether or not zone redundancy is enabled for this container registry." } }, - "enableNfsV3": { - "type": "bool", - "defaultValue": false, + "replications": { + "type": "array", + "items": { + "$ref": "#/definitions/replicationType" + }, + "nullable": true, "metadata": { - "description": "Optional. If true, enables NFS 3.0 support for the storage account. Requires enableHierarchicalNamespace to be true." + "description": "Optional. All replications to create." } }, - "diagnosticSettings": { + "webhooks": { "type": "array", "items": { - "$ref": "#/definitions/diagnosticSettingFullType" + "$ref": "#/definitions/webhookType" }, "nullable": true, "metadata": { - "description": "Optional. The diagnostic settings of the service." + "description": "Optional. All webhooks to create." } }, "lock": { @@ -25600,6 +21398,13 @@ "description": "Optional. The lock settings of the service." } }, + "managedIdentities": { + "$ref": "#/definitions/managedIdentityAllType", + "nullable": true, + "metadata": { + "description": "Optional. The managed identity definition for this resource." + } + }, "tags": { "type": "object", "nullable": true, @@ -25614,35 +21419,21 @@ "description": "Optional. Enable/Disable usage telemetry for module." } }, - "allowedCopyScope": { - "type": "string", - "defaultValue": "", - "allowedValues": [ - "", - "AAD", - "PrivateLink" - ], - "metadata": { - "description": "Optional. Restrict copy to and from Storage Accounts within an AAD tenant or with Private Links to the same VNet." - } - }, - "publicNetworkAccess": { - "type": "string", - "defaultValue": "", - "allowedValues": [ - "", - "Enabled", - "Disabled" - ], + "diagnosticSettings": { + "type": "array", + "items": { + "$ref": "#/definitions/diagnosticSettingFullType" + }, + "nullable": true, "metadata": { - "description": "Optional. Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set and networkAcls are not set." + "description": "Optional. The diagnostic settings of the service." } }, - "supportsHttpsTrafficOnly": { + "anonymousPullEnabled": { "type": "bool", - "defaultValue": true, + "defaultValue": false, "metadata": { - "description": "Optional. Allows HTTPS traffic only to storage service if sets to true." + "description": "Optional. Enables registry-wide pull from unauthenticated clients. It's in preview and available in the Standard and Premium service tiers." } }, "customerManagedKey": { @@ -25652,29 +21443,34 @@ "description": "Optional. The customer managed key definition." } }, - "sasExpirationPeriod": { - "type": "string", - "defaultValue": "", + "cacheRules": { + "type": "array", + "items": { + "$ref": "#/definitions/cacheRuleType" + }, + "nullable": true, "metadata": { - "description": "Optional. The SAS expiration period. DD.HH:MM:SS." + "description": "Optional. Array of Cache Rules." } }, - "keyType": { - "type": "string", + "credentialSets": { + "type": "array", + "items": { + "$ref": "#/definitions/credentialSetType" + }, "nullable": true, - "allowedValues": [ - "Account", - "Service" - ], "metadata": { - "description": "Optional. The keyType to use with Queue & Table services." + "description": "Optional. Array of Credential Sets." } }, - "secretsExportConfiguration": { - "$ref": "#/definitions/secretsExportConfigurationType", + "scopeMaps": { + "type": "array", + "items": { + "$ref": "#/definitions/scopeMapsType" + }, "nullable": true, "metadata": { - "description": "Optional. Key vault reference and secret settings for the module's secrets export." + "description": "Optional. Scope maps setting." } } }, @@ -25686,34 +21482,19 @@ "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)))))]" } ], - "supportsBlobService": "[or(or(or(equals(parameters('kind'), 'BlockBlobStorage'), equals(parameters('kind'), 'BlobStorage')), equals(parameters('kind'), 'StorageV2')), equals(parameters('kind'), 'Storage'))]", - "supportsFileService": "[or(or(equals(parameters('kind'), 'FileStorage'), equals(parameters('kind'), 'StorageV2')), equals(parameters('kind'), 'Storage'))]", "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", - "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned,UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', 'None')), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", + "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned, UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', 'None')), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", "builtInRoleNames": { + "AcrDelete": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c2f4ef07-c644-48eb-af81-4b1b4947fb11')]", + "AcrImageSigner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6cef56e8-d556-48e5-a04f-b8e64114680f')]", + "AcrPull": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d')]", + "AcrPush": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8311e382-0749-4cb8-b61a-304f252e45ec')]", + "AcrQuarantineReader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'cdda3590-29a3-44f6-95f2-9f980659eb04')]", + "AcrQuarantineWriter": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c8d4ff99-41c3-41a8-9f60-21dfdad59608')]", "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Reader and Data Access": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c12c1c16-33a1-487b-954d-41c89c60f349')]", "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "Storage Account Backup Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e5e2a7ff-d759-4cd2-bb51-3152d37e2eb1')]", - "Storage Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab')]", - "Storage Account Key Operator Service Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '81a9662b-bebf-436f-a333-f67b29880f12')]", - "Storage Blob Data Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe')]", - "Storage Blob Data Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b7e6dc6d-f1e8-4753-8033-0f276bb0955b')]", - "Storage Blob Data Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2a2b9908-6ea1-4ae2-8e65-a410df84e7d1')]", - "Storage Blob Delegator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'db58b8e5-c6ad-4a2a-8342-4190687cbf4a')]", - "Storage File Data Privileged Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '69566ab7-960f-475b-8e7c-b3118f30c6bd')]", - "Storage File Data Privileged Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b8eda974-7b85-4f76-af95-65846b26df6d')]", - "Storage File Data SMB Share Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0c867c2a-1d8c-454a-a3db-ab2ea1bdc8bb')]", - "Storage File Data SMB Share Elevated Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a7264617-510b-434b-a828-9731dc254ea7')]", - "Storage File Data SMB Share Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aba4ae5f-2193-4029-9191-0cb91df5e314')]", - "Storage Queue Data Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '974c5e8b-45b9-4653-ba55-5f855dd0fb88')]", - "Storage Queue Data Message Processor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8a0f0c08-91a1-4084-bc3d-661d67233fed')]", - "Storage Queue Data Message Sender": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c6a89b2d-59bc-44d0-9896-0f6e12d7b80a')]", - "Storage Queue Data Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '19e7f393-937e-4f77-808e-94535e297925')]", - "Storage Table Data Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3')]", - "Storage Table Data Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '76199698-9eea-4c19-bc75-cec21354c6b6')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, @@ -25731,7 +21512,7 @@ "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2024-03-01", - "name": "[format('46d3xbcp.res.storage-storageaccount.{0}.{1}', replace('0.17.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "name": "[format('46d3xbcp.res.containerregistry-registry.{0}.{1}', replace('0.8.4', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -25765,55 +21546,66 @@ "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '////'), '/')[4]]", "name": "[last(split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), 'dummyMsi'), '/'))]" }, - "storageAccount": { - "type": "Microsoft.Storage/storageAccounts", - "apiVersion": "2023-05-01", + "registry": { + "type": "Microsoft.ContainerRegistry/registries", + "apiVersion": "2023-06-01-preview", "name": "[parameters('name')]", "location": "[parameters('location')]", - "kind": "[parameters('kind')]", - "sku": { - "name": "[parameters('skuName')]" - }, "identity": "[variables('identity')]", "tags": "[parameters('tags')]", + "sku": { + "name": "[parameters('acrSku')]" + }, "properties": { - "allowSharedKeyAccess": "[parameters('allowSharedKeyAccess')]", - "defaultToOAuthAuthentication": "[parameters('defaultToOAuthAuthentication')]", - "allowCrossTenantReplication": "[parameters('allowCrossTenantReplication')]", - "allowedCopyScope": "[if(not(empty(parameters('allowedCopyScope'))), parameters('allowedCopyScope'), null())]", - "customDomain": { - "name": "[parameters('customDomainName')]", - "useSubDomainName": "[parameters('customDomainUseSubDomainName')]" + "anonymousPullEnabled": "[parameters('anonymousPullEnabled')]", + "adminUserEnabled": "[parameters('acrAdminUserEnabled')]", + "encryption": "[if(not(empty(parameters('customerManagedKey'))), createObject('status', 'enabled', 'keyVaultProperties', createObject('identity', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), ''))), reference('cMKUserAssignedIdentity').clientId, null()), 'keyIdentifier', if(not(empty(tryGet(parameters('customerManagedKey'), 'keyVersion'))), format('{0}/{1}', reference('cMKKeyVault::cMKKey').keyUri, tryGet(parameters('customerManagedKey'), 'keyVersion')), if(coalesce(tryGet(parameters('customerManagedKey'), 'autoRotationEnabled'), true()), reference('cMKKeyVault::cMKKey').keyUri, reference('cMKKeyVault::cMKKey').keyUriWithVersion)))), null())]", + "policies": { + "azureADAuthenticationAsArmPolicy": { + "status": "[parameters('azureADAuthenticationAsArmPolicyStatus')]" + }, + "exportPolicy": "[if(equals(parameters('acrSku'), 'Premium'), createObject('status', parameters('exportPolicyStatus')), null())]", + "quarantinePolicy": "[if(equals(parameters('acrSku'), 'Premium'), createObject('status', parameters('quarantinePolicyStatus')), null())]", + "trustPolicy": "[if(equals(parameters('acrSku'), 'Premium'), createObject('type', 'Notary', 'status', parameters('trustPolicyStatus')), null())]", + "retentionPolicy": "[if(equals(parameters('acrSku'), 'Premium'), createObject('days', parameters('retentionPolicyDays'), 'status', parameters('retentionPolicyStatus')), null())]", + "softDeletePolicy": { + "retentionDays": "[parameters('softDeletePolicyDays')]", + "status": "[parameters('softDeletePolicyStatus')]" + } }, - "dnsEndpointType": "[if(not(empty(parameters('dnsEndpointType'))), parameters('dnsEndpointType'), null())]", - "isLocalUserEnabled": "[parameters('isLocalUserEnabled')]", - "encryption": "[union(createObject('keySource', if(not(empty(parameters('customerManagedKey'))), 'Microsoft.Keyvault', 'Microsoft.Storage'), 'services', createObject('blob', if(variables('supportsBlobService'), createObject('enabled', true()), null()), 'file', if(variables('supportsFileService'), createObject('enabled', true()), null()), 'table', createObject('enabled', true(), 'keyType', parameters('keyType')), 'queue', createObject('enabled', true(), 'keyType', parameters('keyType'))), 'keyvaultproperties', if(not(empty(parameters('customerManagedKey'))), createObject('keyname', parameters('customerManagedKey').keyName, 'keyvaulturi', reference('cMKKeyVault').vaultUri, 'keyversion', if(not(empty(tryGet(parameters('customerManagedKey'), 'keyVersion'))), parameters('customerManagedKey').keyVersion, if(coalesce(tryGet(parameters('customerManagedKey'), 'autoRotationEnabled'), true()), null(), last(split(reference('cMKKeyVault::cMKKey').keyUriWithVersion, '/'))))), null()), 'identity', createObject('userAssignedIdentity', if(not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'))), extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '//'), '/')[2], split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '////'), '/')[4]), 'Microsoft.ManagedIdentity/userAssignedIdentities', last(split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), 'dummyMsi'), '/'))), null()))), if(parameters('requireInfrastructureEncryption'), createObject('requireInfrastructureEncryption', if(not(equals(parameters('kind'), 'Storage')), parameters('requireInfrastructureEncryption'), null())), createObject()))]", - "accessTier": "[if(and(not(equals(parameters('kind'), 'Storage')), not(equals(parameters('kind'), 'BlockBlobStorage'))), parameters('accessTier'), null())]", - "sasPolicy": "[if(not(empty(parameters('sasExpirationPeriod'))), createObject('expirationAction', 'Log', 'sasExpirationPeriod', parameters('sasExpirationPeriod')), null())]", - "supportsHttpsTrafficOnly": "[parameters('supportsHttpsTrafficOnly')]", - "isHnsEnabled": "[parameters('enableHierarchicalNamespace')]", - "isSftpEnabled": "[parameters('enableSftp')]", - "isNfsV3Enabled": "[if(parameters('enableNfsV3'), parameters('enableNfsV3'), '')]", - "largeFileSharesState": "[if(or(equals(parameters('skuName'), 'Standard_LRS'), equals(parameters('skuName'), 'Standard_ZRS')), parameters('largeFileSharesState'), null())]", - "minimumTlsVersion": "[parameters('minimumTlsVersion')]", - "networkAcls": "[if(not(empty(parameters('networkAcls'))), union(createObject('resourceAccessRules', tryGet(parameters('networkAcls'), 'resourceAccessRules'), 'defaultAction', coalesce(tryGet(parameters('networkAcls'), 'defaultAction'), 'Deny'), 'virtualNetworkRules', tryGet(parameters('networkAcls'), 'virtualNetworkRules'), 'ipRules', tryGet(parameters('networkAcls'), 'ipRules')), if(contains(parameters('networkAcls'), 'bypass'), createObject('bypass', tryGet(parameters('networkAcls'), 'bypass')), createObject())), createObject('bypass', 'AzureServices', 'defaultAction', 'Deny'))]", - "allowBlobPublicAccess": "[parameters('allowBlobPublicAccess')]", - "publicNetworkAccess": "[if(not(empty(parameters('publicNetworkAccess'))), parameters('publicNetworkAccess'), if(and(not(empty(parameters('privateEndpoints'))), empty(parameters('networkAcls'))), 'Disabled', null()))]", - "azureFilesIdentityBasedAuthentication": "[if(not(empty(parameters('azureFilesIdentityBasedAuthentication'))), parameters('azureFilesIdentityBasedAuthentication'), null())]" + "dataEndpointEnabled": "[parameters('dataEndpointEnabled')]", + "publicNetworkAccess": "[if(not(empty(parameters('publicNetworkAccess'))), parameters('publicNetworkAccess'), if(and(not(empty(parameters('privateEndpoints'))), empty(parameters('networkRuleSetIpRules'))), 'Disabled', null()))]", + "networkRuleBypassOptions": "[parameters('networkRuleBypassOptions')]", + "networkRuleSet": "[if(not(empty(parameters('networkRuleSetIpRules'))), createObject('defaultAction', parameters('networkRuleSetDefaultAction'), 'ipRules', parameters('networkRuleSetIpRules')), null())]", + "zoneRedundancy": "[if(equals(parameters('acrSku'), 'Premium'), parameters('zoneRedundancy'), null())]" }, "dependsOn": [ "cMKKeyVault::cMKKey", - "cMKKeyVault" + "cMKUserAssignedIdentity" ] }, - "storageAccount_diagnosticSettings": { + "registry_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.ContainerRegistry/registries/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "registry" + ] + }, + "registry_diagnosticSettings": { "copy": { - "name": "storageAccount_diagnosticSettings", + "name": "registry_diagnosticSettings", "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" }, "type": "Microsoft.Insights/diagnosticSettings", "apiVersion": "2021-05-01-preview", - "scope": "[format('Microsoft.Storage/storageAccounts/{0}', parameters('name'))]", + "scope": "[format('Microsoft.ContainerRegistry/registries/{0}', parameters('name'))]", "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", "properties": { "copy": [ @@ -25825,6 +21617,15 @@ "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", "timeGrain": null } + }, + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } } ], "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", @@ -25835,32 +21636,18 @@ "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" }, "dependsOn": [ - "storageAccount" - ] - }, - "storageAccount_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.Storage/storageAccounts/{0}', parameters('name'))]", - "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", - "properties": { - "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", - "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" - }, - "dependsOn": [ - "storageAccount" + "registry" ] }, - "storageAccount_roleAssignments": { + "registry_roleAssignments": { "copy": { - "name": "storageAccount_roleAssignments", + "name": "registry_roleAssignments", "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Storage/storageAccounts/{0}', parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Storage/storageAccounts', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "scope": "[format('Microsoft.ContainerRegistry/registries/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.ContainerRegistry/registries', 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]", @@ -25871,17 +21658,17 @@ "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" }, "dependsOn": [ - "storageAccount" + "registry" ] }, - "storageAccount_privateEndpoints": { + "registry_scopeMaps": { "copy": { - "name": "storageAccount_privateEndpoints", - "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]" + "name": "registry_scopeMaps", + "count": "[length(coalesce(parameters('scopeMaps'), createArray()))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-storageAccount-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "name": "[format('{0}-Registry-Scope-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -25889,42 +21676,16 @@ "mode": "Incremental", "parameters": { "name": { - "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'name'), format('pep-{0}-{1}-{2}', last(split(resourceId('Microsoft.Storage/storageAccounts', parameters('name')), '/')), coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service, copyIndex()))]" - }, - "privateLinkServiceConnections": "[if(not(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true())), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.Storage/storageAccounts', parameters('name')), '/')), coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service, copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.Storage/storageAccounts', parameters('name')), 'groupIds', createArray(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service))))), createObject('value', null()))]", - "manualPrivateLinkServiceConnections": "[if(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true()), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.Storage/storageAccounts', parameters('name')), '/')), coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service, copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.Storage/storageAccounts', parameters('name')), 'groupIds', createArray(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service), 'requestMessage', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualConnectionRequestMessage'), 'Manual approval required.'))))), createObject('value', null()))]", - "subnetResourceId": { - "value": "[coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId]" - }, - "enableTelemetry": { - "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'enableTelemetry'), parameters('enableTelemetry'))]" - }, - "location": { - "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'location'), reference(split(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location)]" - }, - "lock": { - "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" - }, - "privateDnsZoneGroup": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroup')]" - }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'roleAssignments')]" - }, - "tags": { - "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" - }, - "customDnsConfigs": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customDnsConfigs')]" + "value": "[tryGet(coalesce(parameters('scopeMaps'), createArray())[copyIndex()], 'name')]" }, - "ipConfigurations": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'ipConfigurations')]" + "actions": { + "value": "[coalesce(parameters('scopeMaps'), createArray())[copyIndex()].actions]" }, - "applicationSecurityGroupResourceIds": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'applicationSecurityGroupResourceIds')]" + "description": { + "value": "[tryGet(coalesce(parameters('scopeMaps'), createArray())[copyIndex()], 'description')]" }, - "customNetworkInterfaceName": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customNetworkInterfaceName')]" + "registryName": { + "value": "[parameters('name')]" } }, "template": { @@ -25934,268 +21695,320 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "15954548978129725136" + "version": "0.33.93.31351", + "templateHash": "11112300500664950599" }, - "name": "Private Endpoints", - "description": "This module deploys a Private Endpoint." + "name": "Container Registries scopeMaps", + "description": "This module deploys an Azure Container Registry (ACR) scopeMap." }, - "definitions": { - "privateDnsZoneGroupType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the Private DNS Zone Group." - } - }, - "privateDnsZoneGroupConfigs": { - "type": "array", - "items": { - "$ref": "#/definitions/privateDnsZoneGroupConfigType" - }, - "metadata": { - "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." - } - } - }, + "parameters": { + "registryName": { + "type": "string", "metadata": { - "__bicep_export!": true + "description": "Conditional. The name of the parent registry. Required if the template is used in a standalone deployment." } }, - "ipConfigurationType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the resource that is unique within a resource group." - } - }, - "properties": { - "type": "object", - "properties": { - "groupId": { - "type": "string", - "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." - } - }, - "memberName": { - "type": "string", - "metadata": { - "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." - } - }, - "privateIPAddress": { - "type": "string", - "metadata": { - "description": "Required. A private IP address obtained from the private endpoint's subnet." - } - } - }, - "metadata": { - "description": "Required. Properties of private endpoint IP configurations." - } - } + "name": { + "type": "string", + "defaultValue": "[format('{0}-scopemaps', parameters('registryName'))]", + "metadata": { + "description": "Optional. The name of the scope map." + } + }, + "actions": { + "type": "array", + "items": { + "type": "string" }, "metadata": { - "__bicep_export!": true + "description": "Required. The list of scoped permissions for registry artifacts." } }, - "privateLinkServiceConnectionType": { - "type": "object", + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The user friendly description of the scope map." + } + } + }, + "resources": { + "registry": { + "existing": true, + "type": "Microsoft.ContainerRegistry/registries", + "apiVersion": "2023-06-01-preview", + "name": "[parameters('registryName')]" + }, + "scopeMap": { + "type": "Microsoft.ContainerRegistry/registries/scopeMaps", + "apiVersion": "2023-06-01-preview", + "name": "[format('{0}/{1}', parameters('registryName'), parameters('name'))]", "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the private link service connection." - } - }, - "properties": { - "type": "object", - "properties": { - "groupIds": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." - } - }, - "privateLinkServiceId": { - "type": "string", - "metadata": { - "description": "Required. The resource id of private link service." - } - }, - "requestMessage": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." - } - } - }, - "metadata": { - "description": "Required. Properties of private link service connection." - } - } + "actions": "[parameters('actions')]", + "description": "[parameters('description')]" + } + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the scope map." }, + "value": "[parameters('name')]" + }, + "resourceGroupName": { + "type": "string", "metadata": { - "__bicep_export!": true + "description": "The name of the resource group the scope map was created in." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the scope map." + }, + "value": "[resourceId('Microsoft.ContainerRegistry/registries/scopeMaps', parameters('registryName'), parameters('name'))]" + } + } + } + }, + "dependsOn": [ + "registry" + ] + }, + "registry_replications": { + "copy": { + "name": "registry_replications", + "count": "[length(coalesce(parameters('replications'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Registry-Replication-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(parameters('replications'), createArray())[copyIndex()].name]" + }, + "registryName": { + "value": "[parameters('name')]" + }, + "location": { + "value": "[coalesce(parameters('replications'), createArray())[copyIndex()].location]" + }, + "regionEndpointEnabled": { + "value": "[tryGet(coalesce(parameters('replications'), createArray())[copyIndex()], 'regionEndpointEnabled')]" + }, + "zoneRedundancy": { + "value": "[tryGet(coalesce(parameters('replications'), createArray())[copyIndex()], 'zoneRedundancy')]" + }, + "tags": { + "value": "[coalesce(tryGet(coalesce(parameters('replications'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + } + }, + "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.33.93.31351", + "templateHash": "6036875058945996178" + }, + "name": "Azure Container Registry (ACR) Replications", + "description": "This module deploys an Azure Container Registry (ACR) Replication." + }, + "parameters": { + "registryName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent registry. Required if the template is used in a standalone deployment." } }, - "customDnsConfigType": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the replication." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "tags": { "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "regionEndpointEnabled": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Specifies whether the replication regional endpoint is enabled. Requests will not be routed to a replication whose regional endpoint is disabled, however its data will continue to be synced with other replications." + } + }, + "zoneRedundancy": { + "type": "string", + "defaultValue": "Disabled", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "metadata": { + "description": "Optional. Whether or not zone redundancy is enabled for this container registry." + } + } + }, + "resources": { + "registry": { + "existing": true, + "type": "Microsoft.ContainerRegistry/registries", + "apiVersion": "2023-06-01-preview", + "name": "[parameters('registryName')]" + }, + "replication": { + "type": "Microsoft.ContainerRegistry/registries/replications", + "apiVersion": "2023-06-01-preview", + "name": "[format('{0}/{1}', parameters('registryName'), parameters('name'))]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", "properties": { - "fqdn": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. FQDN that resolves to private endpoint IP address." - } - }, - "ipAddresses": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. A list of private IP addresses of the private endpoint." - } - } + "regionEndpointEnabled": "[parameters('regionEndpointEnabled')]", + "zoneRedundancy": "[parameters('zoneRedundancy')]" + } + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the replication." }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", "metadata": { - "__bicep_export!": true - } + "description": "The resource ID of the replication." + }, + "value": "[resourceId('Microsoft.ContainerRegistry/registries/replications', parameters('registryName'), parameters('name'))]" }, - "lockType": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the replication was created in." + }, + "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('replication', '2023-06-01-preview', 'full').location]" + } + } + } + }, + "dependsOn": [ + "registry" + ] + }, + "registry_credentialSets": { + "copy": { + "name": "registry_credentialSets", + "count": "[length(coalesce(parameters('credentialSets'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Registry-CredentialSet-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(parameters('credentialSets'), createArray())[copyIndex()].name]" + }, + "registryName": { + "value": "[parameters('name')]" + }, + "managedIdentities": { + "value": "[coalesce(parameters('credentialSets'), createArray())[copyIndex()].managedIdentities]" + }, + "authCredentials": { + "value": "[coalesce(parameters('credentialSets'), createArray())[copyIndex()].authCredentials]" + }, + "loginServer": { + "value": "[coalesce(parameters('credentialSets'), createArray())[copyIndex()].loginServer]" + } + }, + "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.33.93.31351", + "templateHash": "15848218260506856293" + }, + "name": "Container Registries Credential Sets", + "description": "This module deploys an ACR Credential Set." + }, + "definitions": { + "authCredentialsType": { "type": "object", "properties": { "name": { "type": "string", - "nullable": true, "metadata": { - "description": "Optional. Specify the name of lock." + "description": "Required. The name of the credential." } }, - "kind": { - "type": "string", - "allowedValues": [ - "CanNotDelete", - "None", - "ReadOnly" - ], - "nullable": true, - "metadata": { - "description": "Optional. Specify the type of 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.5.1" - } - } - }, - "privateDnsZoneGroupConfigType": { - "type": "object", - "properties": { - "name": { + "usernameSecretIdentifier": { "type": "string", - "nullable": true, "metadata": { - "description": "Optional. The name of the private DNS zone group config." + "description": "Required. KeyVault Secret URI for accessing the username." } }, - "privateDnsZoneResourceId": { + "passwordSecretIdentifier": { "type": "string", "metadata": { - "description": "Required. The resource id of the private DNS zone." + "description": "Required. KeyVault Secret URI for accessing the password." } } }, "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "private-dns-zone-group/main.bicep" - } + "__bicep_export!": true, + "description": "The type for auth credentials." } }, - "roleAssignmentType": { + "managedIdentityOnlySysAssignedType": { "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", + "systemAssigned": { + "type": "bool", "nullable": true, "metadata": { - "description": "Optional. The Resource Id of the delegated managed identity resource." + "description": "Optional. Enables system assigned managed identity on the resource." } } }, "metadata": { - "description": "An AVM-aligned type for a role assignment.", + "description": "An AVM-aligned type for a managed identity configuration. To be used if only system-assigned identities are supported by the resource provider.", "__bicep_imported_from!": { "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } @@ -26203,556 +22016,265 @@ } }, "parameters": { - "name": { + "registryName": { "type": "string", "metadata": { - "description": "Required. Name of the private endpoint resource to create." + "description": "Conditional. The name of the parent registry. Required if the template is used in a standalone deployment." } }, - "subnetResourceId": { + "name": { "type": "string", "metadata": { - "description": "Required. Resource ID of the subnet where the endpoint needs to be created." - } - }, - "applicationSecurityGroupResourceIds": { - "type": "array", - "items": { - "type": "string" - }, - "nullable": true, - "metadata": { - "description": "Optional. Application security groups in which the private endpoint IP configuration is included." + "description": "Required. The name of the credential set." } }, - "customNetworkInterfaceName": { - "type": "string", + "managedIdentities": { + "$ref": "#/definitions/managedIdentityOnlySysAssignedType", "nullable": true, "metadata": { - "description": "Optional. The custom name of the network interface attached to the private endpoint." + "description": "Optional. The managed identity definition for this resource." } }, - "ipConfigurations": { + "authCredentials": { "type": "array", "items": { - "$ref": "#/definitions/ipConfigurationType" + "$ref": "#/definitions/authCredentialsType" }, - "nullable": true, "metadata": { - "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." + "description": "Required. List of authentication credentials stored for an upstream. Usually consists of a primary and an optional secondary credential." } }, - "privateDnsZoneGroup": { - "$ref": "#/definitions/privateDnsZoneGroupType", - "nullable": true, + "loginServer": { + "type": "string", "metadata": { - "description": "Optional. The private DNS zone group to configure for the private endpoint." + "description": "Required. The credentials are stored for this upstream or login server." } + } + }, + "variables": { + "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), 'SystemAssigned', null())), null())]" + }, + "resources": { + "registry": { + "existing": true, + "type": "Microsoft.ContainerRegistry/registries", + "apiVersion": "2023-06-01-preview", + "name": "[parameters('registryName')]" }, - "location": { + "credentialSet": { + "type": "Microsoft.ContainerRegistry/registries/credentialSets", + "apiVersion": "2023-11-01-preview", + "name": "[format('{0}/{1}', parameters('registryName'), parameters('name'))]", + "identity": "[variables('identity')]", + "properties": { + "authCredentials": "[parameters('authCredentials')]", + "loginServer": "[parameters('loginServer')]" + } + } + }, + "outputs": { + "name": { "type": "string", - "defaultValue": "[resourceGroup().location]", "metadata": { - "description": "Optional. Location for all Resources." - } + "description": "The Name of the Credential Set." + }, + "value": "[parameters('name')]" }, - "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", - "nullable": true, - "metadata": { - "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." - } - }, - "customDnsConfigs": { - "type": "array", - "items": { - "$ref": "#/definitions/customDnsConfigType" - }, - "nullable": true, - "metadata": { - "description": "Optional. Custom DNS configurations." - } - }, - "manualPrivateLinkServiceConnections": { - "type": "array", - "items": { - "$ref": "#/definitions/privateLinkServiceConnectionType" - }, - "nullable": true, - "metadata": { - "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." - } - }, - "privateLinkServiceConnections": { - "type": "array", - "items": { - "$ref": "#/definitions/privateLinkServiceConnectionType" - }, - "nullable": true, - "metadata": { - "description": "Conditional. A grouping of information about the connection to the remote resource. Required if `manualPrivateLinkServiceConnections` is empty." - } - }, - "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": "2024-03-01", - "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.10.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": "2023-11-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": "[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": "2022-09-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.33.13.18514", - "templateHash": "5440815542537978381" - }, - "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 - } - } - }, - "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." - } - } - }, - "variables": { - "copy": [ - { - "name": "privateDnsZoneConfigsVar", - "count": "[length(parameters('privateDnsZoneConfigs'))]", - "input": { - "name": "[coalesce(tryGet(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')], 'name'), last(split(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId, '/')))]", - "properties": { - "privateDnsZoneId": "[parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId]" - } - } - } - ] - }, - "resources": { - "privateEndpoint": { - "existing": true, - "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2023-11-01", - "name": "[parameters('privateEndpointName')]" - }, - "privateDnsZoneGroup": { - "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2023-11-01", - "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", - "properties": { - "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" - } - } - }, - "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." + "description": "The name of the Credential Set." }, "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', '2023-11-01', 'full').location]" - }, - "customDnsConfigs": { - "type": "array", - "items": { - "$ref": "#/definitions/customDnsConfigType" - }, - "metadata": { - "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." + "description": "The resource ID of the Credential Set." }, - "value": "[map(reference('privateEndpoint').networkInterfaces, lambda('nic', lambdaVariables('nic').id))]" + "value": "[resourceId('Microsoft.ContainerRegistry/registries/credentialSets', parameters('registryName'), parameters('name'))]" }, - "groupId": { + "systemAssignedMIPrincipalId": { "type": "string", "nullable": true, "metadata": { - "description": "The group Id for the private endpoint Group." + "description": "The principal ID of the system assigned identity." }, - "value": "[coalesce(tryGet(tryGet(tryGet(tryGet(reference('privateEndpoint'), 'manualPrivateLinkServiceConnections'), 0, 'properties'), 'groupIds'), 0), tryGet(tryGet(tryGet(tryGet(reference('privateEndpoint'), 'privateLinkServiceConnections'), 0, 'properties'), 'groupIds'), 0))]" + "value": "[tryGet(tryGet(reference('credentialSet', '2023-11-01-preview', 'full'), 'identity'), 'principalId')]" } } } }, "dependsOn": [ - "storageAccount" + "registry" ] }, - "storageAccount_managementPolicies": { - "condition": "[not(empty(coalesce(parameters('managementPolicyRules'), createArray())))]", + "registry_cacheRules": { + "copy": { + "name": "registry_cacheRules", + "count": "[length(coalesce(parameters('cacheRules'), createArray()))]" + }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-Storage-ManagementPolicies', uniqueString(deployment().name, parameters('location')))]", + "name": "[format('{0}-Registry-Cache-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "storageAccountName": { + "registryName": { "value": "[parameters('name')]" }, - "rules": { - "value": "[coalesce(parameters('managementPolicyRules'), createArray())]" + "sourceRepository": { + "value": "[coalesce(parameters('cacheRules'), createArray())[copyIndex()].sourceRepository]" + }, + "name": { + "value": "[tryGet(coalesce(parameters('cacheRules'), createArray())[copyIndex()], 'name')]" + }, + "targetRepository": { + "value": "[coalesce(tryGet(coalesce(parameters('cacheRules'), createArray())[copyIndex()], 'targetRepository'), coalesce(parameters('cacheRules'), createArray())[copyIndex()].sourceRepository)]" + }, + "credentialSetResourceId": { + "value": "[tryGet(coalesce(parameters('cacheRules'), createArray())[copyIndex()], 'credentialSetResourceId')]" } }, "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.33.13.18514", - "templateHash": "4967204006599351003" + "version": "0.33.93.31351", + "templateHash": "3783697279882479947" }, - "name": "Storage Account Management Policies", - "description": "This module deploys a Storage Account Management Policy." + "name": "Container Registries Cache", + "description": "Cache for Azure Container Registry (Preview) feature allows users to cache container images in a private container registry. Cache for ACR, is a preview feature available in Basic, Standard, and Premium service tiers ([ref](https://learn.microsoft.com/en-us/azure/container-registry/tutorial-registry-cache))." }, "parameters": { - "storageAccountName": { + "registryName": { "type": "string", - "maxLength": 24, "metadata": { - "description": "Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment." + "description": "Conditional. The name of the parent registry. Required if the template is used in a standalone deployment." } }, - "rules": { - "type": "array", + "name": { + "type": "string", + "defaultValue": "[replace(replace(replace(parameters('sourceRepository'), '/', '-'), '.', '-'), '*', '')]", "metadata": { - "description": "Required. The Storage Account ManagementPolicies Rules." + "description": "Optional. The name of the cache rule. Will be derived from the source repository name if not defined." + } + }, + "sourceRepository": { + "type": "string", + "metadata": { + "description": "Required. Source repository pulled from upstream." + } + }, + "targetRepository": { + "type": "string", + "defaultValue": "[parameters('sourceRepository')]", + "metadata": { + "description": "Optional. Target repository specified in docker pull command. E.g.: docker pull myregistry.azurecr.io/{targetRepository}:{tag}." + } + }, + "credentialSetResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the credential store which is associated with the cache rule." } } }, - "resources": [ - { - "type": "Microsoft.Storage/storageAccounts/managementPolicies", - "apiVersion": "2023-01-01", - "name": "[format('{0}/{1}', parameters('storageAccountName'), 'default')]", + "resources": { + "registry": { + "existing": true, + "type": "Microsoft.ContainerRegistry/registries", + "apiVersion": "2023-06-01-preview", + "name": "[parameters('registryName')]" + }, + "cacheRule": { + "type": "Microsoft.ContainerRegistry/registries/cacheRules", + "apiVersion": "2023-06-01-preview", + "name": "[format('{0}/{1}', parameters('registryName'), parameters('name'))]", "properties": { - "policy": { - "rules": "[parameters('rules')]" - } + "sourceRepository": "[parameters('sourceRepository')]", + "targetRepository": "[parameters('targetRepository')]", + "credentialSetResourceId": "[parameters('credentialSetResourceId')]" } } - ], + }, "outputs": { - "resourceId": { + "name": { "type": "string", "metadata": { - "description": "The resource ID of the deployed management policy." + "description": "The Name of the Cache Rule." }, - "value": "default" + "value": "[parameters('name')]" }, - "name": { + "resourceGroupName": { "type": "string", "metadata": { - "description": "The name of the deployed management policy." + "description": "The name of the Cache Rule." }, - "value": "default" + "value": "[resourceGroup().name]" }, - "resourceGroupName": { + "resourceId": { "type": "string", "metadata": { - "description": "The resource group of the deployed management policy." + "description": "The resource ID of the Cache Rule." }, - "value": "[resourceGroup().name]" + "value": "[resourceId('Microsoft.ContainerRegistry/registries/cacheRules', parameters('registryName'), parameters('name'))]" } } } }, "dependsOn": [ - "storageAccount", - "storageAccount_blobServices" + "registry", + "registry_credentialSets" ] }, - "storageAccount_localUsers": { + "registry_webhooks": { "copy": { - "name": "storageAccount_localUsers", - "count": "[length(coalesce(parameters('localUsers'), createArray()))]" + "name": "registry_webhooks", + "count": "[length(coalesce(parameters('webhooks'), createArray()))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-Storage-LocalUsers-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "name": "[format('{0}-Registry-Webhook-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "storageAccountName": { + "name": { + "value": "[coalesce(parameters('webhooks'), createArray())[copyIndex()].name]" + }, + "registryName": { "value": "[parameters('name')]" }, - "name": { - "value": "[coalesce(parameters('localUsers'), createArray())[copyIndex()].name]" + "location": { + "value": "[coalesce(tryGet(coalesce(parameters('webhooks'), createArray())[copyIndex()], 'location'), parameters('location'))]" }, - "hasSshKey": { - "value": "[coalesce(parameters('localUsers'), createArray())[copyIndex()].hasSshKey]" + "action": { + "value": "[tryGet(coalesce(parameters('webhooks'), createArray())[copyIndex()], 'action')]" }, - "hasSshPassword": { - "value": "[coalesce(parameters('localUsers'), createArray())[copyIndex()].hasSshPassword]" + "customHeaders": { + "value": "[tryGet(coalesce(parameters('webhooks'), createArray())[copyIndex()], 'customHeaders')]" }, - "permissionScopes": { - "value": "[coalesce(parameters('localUsers'), createArray())[copyIndex()].permissionScopes]" + "scope": { + "value": "[tryGet(coalesce(parameters('webhooks'), createArray())[copyIndex()], 'scope')]" }, - "hasSharedKey": { - "value": "[tryGet(coalesce(parameters('localUsers'), createArray())[copyIndex()], 'hasSharedKey')]" + "status": { + "value": "[tryGet(coalesce(parameters('webhooks'), createArray())[copyIndex()], 'status')]" }, - "homeDirectory": { - "value": "[tryGet(coalesce(parameters('localUsers'), createArray())[copyIndex()], 'homeDirectory')]" + "serviceUri": { + "value": "[coalesce(parameters('webhooks'), createArray())[copyIndex()].serviceUri]" }, - "sshAuthorizedKeys": { - "value": "[tryGet(coalesce(parameters('localUsers'), createArray())[copyIndex()], 'sshAuthorizedKeys')]" + "tags": { + "value": "[coalesce(tryGet(coalesce(parameters('webhooks'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" } }, "template": { @@ -26762,235 +22284,220 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "2528560857012083896" + "version": "0.33.93.31351", + "templateHash": "10084997815751263562" }, - "name": "Storage Account Local Users", - "description": "This module deploys a Storage Account Local User, which is used for SFTP authentication." - }, - "definitions": { - "sshAuthorizedKeyType": { - "type": "object", - "properties": { - "description": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Description used to store the function/usage of the key." - } - }, - "key": { - "type": "securestring", - "metadata": { - "description": "Required. SSH public key base64 encoded. The format should be: '{keyType} {keyData}', e.g. ssh-rsa AAAABBBB." - } - } - }, - "metadata": { - "__bicep_export!": true - } - }, - "permissionScopeType": { - "type": "object", - "properties": { - "permissions": { - "type": "string", - "metadata": { - "description": "Required. The permissions for the local user. Possible values include: Read (r), Write (w), Delete (d), List (l), and Create (c)." - } - }, - "resourceName": { - "type": "string", - "metadata": { - "description": "Required. The name of resource, normally the container name or the file share name, used by the local user." - } - }, - "service": { - "type": "string", - "metadata": { - "description": "Required. The service used by the local user, e.g. blob, file." - } - } - }, - "metadata": { - "__bicep_export!": true - } - } + "name": "Azure Container Registry (ACR) Webhooks", + "description": "This module deploys an Azure Container Registry (ACR) Webhook." }, "parameters": { - "storageAccountName": { + "registryName": { "type": "string", - "maxLength": 24, "metadata": { - "description": "Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment." + "description": "Conditional. The name of the parent registry. Required if the template is used in a standalone deployment." } }, "name": { "type": "string", + "defaultValue": "[format('{0}webhook', parameters('registryName'))]", + "minLength": 5, + "maxLength": 50, "metadata": { - "description": "Required. The name of the local user used for SFTP Authentication." + "description": "Optional. The name of the registry webhook." } }, - "hasSharedKey": { - "type": "bool", - "defaultValue": false, + "serviceUri": { + "type": "string", "metadata": { - "description": "Optional. Indicates whether shared key exists. Set it to false to remove existing shared key." + "description": "Required. The service URI for the webhook to post notifications." } }, - "hasSshKey": { - "type": "bool", + "status": { + "type": "string", + "defaultValue": "enabled", + "allowedValues": [ + "disabled", + "enabled" + ], "metadata": { - "description": "Required. Indicates whether SSH key exists. Set it to false to remove existing SSH key." + "description": "Optional. The status of the webhook at the time the operation was called." } }, - "hasSshPassword": { - "type": "bool", + "action": { + "type": "array", + "items": { + "type": "string" + }, + "defaultValue": [ + "chart_delete", + "chart_push", + "delete", + "push", + "quarantine" + ], "metadata": { - "description": "Required. Indicates whether SSH password exists. Set it to false to remove existing SSH password." + "description": "Optional. The list of actions that trigger the webhook to post notifications." } }, - "homeDirectory": { + "location": { "type": "string", - "defaultValue": "", + "defaultValue": "[resourceGroup().location]", "metadata": { - "description": "Optional. The local user home directory." + "description": "Optional. Location for all resources." } }, - "permissionScopes": { - "type": "array", - "items": { - "$ref": "#/definitions/permissionScopeType" - }, + "tags": { + "type": "object", + "nullable": true, "metadata": { - "description": "Required. The permission scopes of the local user." + "description": "Optional. Tags of the resource." } }, - "sshAuthorizedKeys": { - "type": "array", - "items": { - "$ref": "#/definitions/sshAuthorizedKeyType" - }, + "customHeaders": { + "type": "object", "nullable": true, "metadata": { - "description": "Optional. The local user SSH authorized keys for SFTP." + "description": "Optional. Custom headers that will be added to the webhook notifications." + } + }, + "scope": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The scope of repositories where the event can be triggered. For example, 'foo:*' means events for all tags under repository 'foo'. 'foo:bar' means events for 'foo:bar' only. 'foo' is equivalent to 'foo:latest'. Empty means all events." } } }, "resources": { - "storageAccount": { + "registry": { "existing": true, - "type": "Microsoft.Storage/storageAccounts", - "apiVersion": "2023-04-01", - "name": "[parameters('storageAccountName')]" + "type": "Microsoft.ContainerRegistry/registries", + "apiVersion": "2023-06-01-preview", + "name": "[parameters('registryName')]" }, - "localUsers": { - "type": "Microsoft.Storage/storageAccounts/localUsers", - "apiVersion": "2023-04-01", - "name": "[format('{0}/{1}', parameters('storageAccountName'), parameters('name'))]", + "webhook": { + "type": "Microsoft.ContainerRegistry/registries/webhooks", + "apiVersion": "2023-06-01-preview", + "name": "[format('{0}/{1}', parameters('registryName'), parameters('name'))]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", "properties": { - "hasSharedKey": "[parameters('hasSharedKey')]", - "hasSshKey": "[parameters('hasSshKey')]", - "hasSshPassword": "[parameters('hasSshPassword')]", - "homeDirectory": "[parameters('homeDirectory')]", - "permissionScopes": "[parameters('permissionScopes')]", - "sshAuthorizedKeys": "[parameters('sshAuthorizedKeys')]" + "actions": "[parameters('action')]", + "customHeaders": "[parameters('customHeaders')]", + "scope": "[parameters('scope')]", + "serviceUri": "[parameters('serviceUri')]", + "status": "[parameters('status')]" } } }, "outputs": { + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the webhook." + }, + "value": "[resourceId('Microsoft.ContainerRegistry/registries/webhooks', parameters('registryName'), parameters('name'))]" + }, "name": { "type": "string", "metadata": { - "description": "The name of the deployed local user." + "description": "The name of the webhook." }, "value": "[parameters('name')]" }, "resourceGroupName": { "type": "string", "metadata": { - "description": "The resource group of the deployed local user." + "description": "The name of the Azure container registry." }, "value": "[resourceGroup().name]" }, - "resourceId": { + "actions": { + "type": "array", + "metadata": { + "description": "The actions of the webhook." + }, + "value": "[reference('webhook').actions]" + }, + "status": { "type": "string", "metadata": { - "description": "The resource ID of the deployed local user." + "description": "The status of the webhook." }, - "value": "[resourceId('Microsoft.Storage/storageAccounts/localUsers', parameters('storageAccountName'), parameters('name'))]" + "value": "[reference('webhook').status]" + }, + "provistioningState": { + "type": "string", + "metadata": { + "description": "The provisioning state of the webhook." + }, + "value": "[reference('webhook').provisioningState]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('webhook', '2023-06-01-preview', 'full').location]" } } } }, "dependsOn": [ - "storageAccount" + "registry" ] }, - "storageAccount_blobServices": { - "condition": "[not(empty(parameters('blobServices')))]", + "registry_privateEndpoints": { + "copy": { + "name": "registry_privateEndpoints", + "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]" + }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-Storage-BlobServices', uniqueString(deployment().name, parameters('location')))]", + "name": "[format('{0}-registry-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "storageAccountName": { - "value": "[parameters('name')]" - }, - "containers": { - "value": "[tryGet(parameters('blobServices'), 'containers')]" - }, - "automaticSnapshotPolicyEnabled": { - "value": "[tryGet(parameters('blobServices'), 'automaticSnapshotPolicyEnabled')]" - }, - "changeFeedEnabled": { - "value": "[tryGet(parameters('blobServices'), 'changeFeedEnabled')]" - }, - "changeFeedRetentionInDays": { - "value": "[tryGet(parameters('blobServices'), 'changeFeedRetentionInDays')]" - }, - "containerDeleteRetentionPolicyEnabled": { - "value": "[tryGet(parameters('blobServices'), 'containerDeleteRetentionPolicyEnabled')]" - }, - "containerDeleteRetentionPolicyDays": { - "value": "[tryGet(parameters('blobServices'), 'containerDeleteRetentionPolicyDays')]" + "name": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'name'), format('pep-{0}-{1}-{2}', last(split(resourceId('Microsoft.ContainerRegistry/registries', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'registry'), copyIndex()))]" }, - "containerDeleteRetentionPolicyAllowPermanentDelete": { - "value": "[tryGet(parameters('blobServices'), 'containerDeleteRetentionPolicyAllowPermanentDelete')]" + "privateLinkServiceConnections": "[if(not(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true())), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.ContainerRegistry/registries', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'registry'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.ContainerRegistry/registries', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'registry')))))), createObject('value', null()))]", + "manualPrivateLinkServiceConnections": "[if(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true()), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.ContainerRegistry/registries', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'registry'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.ContainerRegistry/registries', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'registry')), 'requestMessage', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualConnectionRequestMessage'), 'Manual approval required.'))))), createObject('value', null()))]", + "subnetResourceId": { + "value": "[coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId]" }, - "corsRules": { - "value": "[tryGet(parameters('blobServices'), 'corsRules')]" + "enableTelemetry": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'enableTelemetry'), parameters('enableTelemetry'))]" }, - "defaultServiceVersion": { - "value": "[tryGet(parameters('blobServices'), 'defaultServiceVersion')]" + "location": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'location'), reference(split(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location)]" }, - "deleteRetentionPolicyAllowPermanentDelete": { - "value": "[tryGet(parameters('blobServices'), 'deleteRetentionPolicyAllowPermanentDelete')]" + "lock": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" }, - "deleteRetentionPolicyEnabled": { - "value": "[tryGet(parameters('blobServices'), 'deleteRetentionPolicyEnabled')]" + "privateDnsZoneGroup": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroup')]" }, - "deleteRetentionPolicyDays": { - "value": "[tryGet(parameters('blobServices'), 'deleteRetentionPolicyDays')]" + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'roleAssignments')]" }, - "isVersioningEnabled": { - "value": "[tryGet(parameters('blobServices'), 'isVersioningEnabled')]" + "tags": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" }, - "lastAccessTimeTrackingPolicyEnabled": { - "value": "[tryGet(parameters('blobServices'), 'lastAccessTimeTrackingPolicyEnabled')]" + "customDnsConfigs": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customDnsConfigs')]" }, - "restorePolicyEnabled": { - "value": "[tryGet(parameters('blobServices'), 'restorePolicyEnabled')]" + "ipConfigurations": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'ipConfigurations')]" }, - "restorePolicyDays": { - "value": "[tryGet(parameters('blobServices'), 'restorePolicyDays')]" + "applicationSecurityGroupResourceIds": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'applicationSecurityGroupResourceIds')]" }, - "diagnosticSettings": { - "value": "[tryGet(parameters('blobServices'), 'diagnosticSettings')]" + "customNetworkInterfaceName": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customNetworkInterfaceName')]" } }, "template": { @@ -27001,189 +22508,267 @@ "_generator": { "name": "bicep", "version": "0.33.13.18514", - "templateHash": "17736639420143155642" + "templateHash": "15954548978129725136" }, - "name": "Storage Account blob Services", - "description": "This module deploys a Storage Account Blob Service." + "name": "Private Endpoints", + "description": "This module deploys a Private Endpoint." }, "definitions": { - "corsRuleType": { + "privateDnsZoneGroupType": { "type": "object", "properties": { - "allowedHeaders": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. A list of headers allowed to be part of the cross-origin request." - } - }, - "allowedMethods": { - "type": "array", - "allowedValues": [ - "CONNECT", - "DELETE", - "GET", - "HEAD", - "MERGE", - "OPTIONS", - "PATCH", - "POST", - "PUT", - "TRACE" - ], + "name": { + "type": "string", + "nullable": true, "metadata": { - "description": "Required. A list of HTTP methods that are allowed to be executed by the origin." + "description": "Optional. The name of the Private DNS Zone Group." } }, - "allowedOrigins": { + "privateDnsZoneGroupConfigs": { "type": "array", "items": { - "type": "string" + "$ref": "#/definitions/privateDnsZoneGroupConfigType" }, "metadata": { - "description": "Required. A list of origin domains that will be allowed via CORS, or \"*\" to allow all domains." + "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." } - }, - "exposedHeaders": { - "type": "array", - "items": { - "type": "string" - }, + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "ipConfigurationType": { + "type": "object", + "properties": { + "name": { + "type": "string", "metadata": { - "description": "Required. A list of response headers to expose to CORS clients." + "description": "Required. The name of the resource that is unique within a resource group." } }, - "maxAgeInSeconds": { - "type": "int", + "properties": { + "type": "object", + "properties": { + "groupId": { + "type": "string", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } + } + }, "metadata": { - "description": "Required. The number of seconds that the client/browser should cache a preflight response." + "description": "Required. Properties of private endpoint IP configurations." } } }, "metadata": { - "__bicep_export!": true, - "description": "The type for a cors rule." + "__bicep_export!": true } }, - "diagnosticSettingFullType": { + "privateLinkServiceConnectionType": { "type": "object", "properties": { "name": { "type": "string", - "nullable": true, "metadata": { - "description": "Optional. The name of the diagnostic setting." + "description": "Required. The name of the private link service connection." } }, - "logCategoriesAndGroups": { - "type": "array", - "items": { - "type": "object", - "properties": { - "category": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." - } - }, - "categoryGroup": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." - } + "properties": { + "type": "object", + "properties": { + "groupIds": { + "type": "array", + "items": { + "type": "string" }, - "enabled": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Enable or disable the category explicitly. Default is `true`." - } + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." + } + }, + "privateLinkServiceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of private link service." + } + }, + "requestMessage": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." } } }, + "metadata": { + "description": "Required. Properties of private link service connection." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "customDnsConfigType": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", "nullable": true, "metadata": { - "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + "description": "Optional. FQDN that resolves to private endpoint IP address." } }, - "metricCategories": { + "ipAddresses": { "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`." - } - } - } + "type": "string" }, + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", "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." + "description": "Optional. Specify the name of lock." } }, - "logAnalyticsDestinationType": { + "kind": { "type": "string", "allowedValues": [ - "AzureDiagnostics", - "Dedicated" + "CanNotDelete", + "None", + "ReadOnly" ], "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." + "description": "Optional. Specify the type of lock." } - }, - "workspaceResourceId": { + } + }, + "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.5.1" + } + } + }, + "privateDnsZoneGroupConfigType": { + "type": "object", + "properties": { + "name": { "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." + "description": "Optional. The name of the private DNS zone group config." } }, - "storageAccountResourceId": { + "privateDnsZoneResourceId": { "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." + "description": "Required. The resource id of the private DNS zone." } - }, - "eventHubAuthorizationRuleResourceId": { + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "private-dns-zone-group/main.bicep" + } + } + }, + "roleAssignmentType": { + "type": "object", + "properties": { + "name": { "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." + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." } }, - "eventHubName": { + "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. 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." + "description": "Optional. The principal type of the assigned principal ID." } }, - "marketplacePartnerResourceId": { + "description": { "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": "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 diagnostic setting. To be used if both logs & metrics are supported by the resource provider.", + "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.5.1" } @@ -27191,273 +22776,244 @@ } }, "parameters": { - "storageAccountName": { + "name": { "type": "string", - "maxLength": 24, - "metadata": { - "description": "Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment." - } - }, - "automaticSnapshotPolicyEnabled": { - "type": "bool", - "defaultValue": false, "metadata": { - "description": "Optional. Automatic Snapshot is enabled if set to true." + "description": "Required. Name of the private endpoint resource to create." } }, - "changeFeedEnabled": { - "type": "bool", - "defaultValue": false, + "subnetResourceId": { + "type": "string", "metadata": { - "description": "Optional. The blob service properties for change feed events. Indicates whether change feed event logging is enabled for the Blob service." + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." } }, - "changeFeedRetentionInDays": { - "type": "int", + "applicationSecurityGroupResourceIds": { + "type": "array", + "items": { + "type": "string" + }, "nullable": true, - "minValue": 1, - "maxValue": 146000, - "metadata": { - "description": "Optional. Indicates whether change feed event logging is enabled for the Blob service. Indicates the duration of changeFeed retention in days. If left blank, it indicates an infinite retention of the change feed." - } - }, - "containerDeleteRetentionPolicyEnabled": { - "type": "bool", - "defaultValue": true, "metadata": { - "description": "Optional. The blob service properties for container soft delete. Indicates whether DeleteRetentionPolicy is enabled." + "description": "Optional. Application security groups in which the private endpoint IP configuration is included." } }, - "containerDeleteRetentionPolicyDays": { - "type": "int", + "customNetworkInterfaceName": { + "type": "string", "nullable": true, - "minValue": 1, - "maxValue": 365, - "metadata": { - "description": "Optional. Indicates the number of days that the deleted item should be retained." - } - }, - "containerDeleteRetentionPolicyAllowPermanentDelete": { - "type": "bool", - "defaultValue": false, "metadata": { - "description": "Optional. This property when set to true allows deletion of the soft deleted blob versions and snapshots. This property cannot be used with blob restore policy. This property only applies to blob service and does not apply to containers or file share." + "description": "Optional. The custom name of the network interface attached to the private endpoint." } }, - "corsRules": { + "ipConfigurations": { "type": "array", "items": { - "$ref": "#/definitions/corsRuleType" + "$ref": "#/definitions/ipConfigurationType" }, "nullable": true, "metadata": { - "description": "Optional. The List of CORS rules. You can include up to five CorsRule elements in the request." - } - }, - "defaultServiceVersion": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Optional. Indicates the default version to use for requests to the Blob service if an incoming request's version is not specified. Possible values include version 2008-10-27 and all more recent versions." - } - }, - "deleteRetentionPolicyEnabled": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Optional. The blob service properties for blob soft delete." + "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." } }, - "deleteRetentionPolicyDays": { - "type": "int", - "defaultValue": 7, - "minValue": 1, - "maxValue": 365, + "privateDnsZoneGroup": { + "$ref": "#/definitions/privateDnsZoneGroupType", + "nullable": true, "metadata": { - "description": "Optional. Indicates the number of days that the deleted blob should be retained." + "description": "Optional. The private DNS zone group to configure for the private endpoint." } }, - "deleteRetentionPolicyAllowPermanentDelete": { - "type": "bool", - "defaultValue": false, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", "metadata": { - "description": "Optional. This property when set to true allows deletion of the soft deleted blob versions and snapshots. This property cannot be used with blob restore policy. This property only applies to blob service and does not apply to containers or file share." + "description": "Optional. Location for all Resources." } }, - "isVersioningEnabled": { - "type": "bool", - "defaultValue": false, + "lock": { + "$ref": "#/definitions/lockType", + "nullable": true, "metadata": { - "description": "Optional. Use versioning to automatically maintain previous versions of your blobs." + "description": "Optional. The lock settings of the service." } }, - "lastAccessTimeTrackingPolicyEnabled": { - "type": "bool", - "defaultValue": false, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, "metadata": { - "description": "Optional. The blob service property to configure last access time based tracking policy. When set to true last access time based tracking is enabled." + "description": "Optional. Array of role assignments to create." } }, - "restorePolicyEnabled": { - "type": "bool", - "defaultValue": false, + "tags": { + "type": "object", + "nullable": true, "metadata": { - "description": "Optional. The blob service properties for blob restore policy. If point-in-time restore is enabled, then versioning, change feed, and blob soft delete must also be enabled." + "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." } }, - "restorePolicyDays": { - "type": "int", - "defaultValue": 6, - "minValue": 1, + "customDnsConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/customDnsConfigType" + }, + "nullable": true, "metadata": { - "description": "Optional. How long this blob can be restored. It should be less than DeleteRetentionPolicy days." + "description": "Optional. Custom DNS configurations." } }, - "containers": { + "manualPrivateLinkServiceConnections": { "type": "array", + "items": { + "$ref": "#/definitions/privateLinkServiceConnectionType" + }, "nullable": true, "metadata": { - "description": "Optional. Blob containers to create." + "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." } }, - "diagnosticSettings": { + "privateLinkServiceConnections": { "type": "array", "items": { - "$ref": "#/definitions/diagnosticSettingFullType" + "$ref": "#/definitions/privateLinkServiceConnectionType" }, "nullable": true, "metadata": { - "description": "Optional. The diagnostic settings of the service." + "description": "Conditional. A grouping of information about the connection to the remote resource. Required if `manualPrivateLinkServiceConnections` is empty." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." } } }, "variables": { - "name": "default" + "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": { - "storageAccount": { - "existing": true, - "type": "Microsoft.Storage/storageAccounts", - "apiVersion": "2022-09-01", - "name": "[parameters('storageAccountName')]" - }, - "blobServices": { - "type": "Microsoft.Storage/storageAccounts/blobServices", - "apiVersion": "2022-09-01", - "name": "[format('{0}/{1}', parameters('storageAccountName'), variables('name'))]", + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.10.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { - "automaticSnapshotPolicyEnabled": "[parameters('automaticSnapshotPolicyEnabled')]", - "changeFeed": "[if(parameters('changeFeedEnabled'), createObject('enabled', true(), 'retentionInDays', parameters('changeFeedRetentionInDays')), null())]", - "containerDeleteRetentionPolicy": { - "enabled": "[parameters('containerDeleteRetentionPolicyEnabled')]", - "days": "[parameters('containerDeleteRetentionPolicyDays')]", - "allowPermanentDelete": "[if(equals(parameters('containerDeleteRetentionPolicyEnabled'), true()), parameters('containerDeleteRetentionPolicyAllowPermanentDelete'), null())]" - }, - "cors": "[if(not(equals(parameters('corsRules'), null())), createObject('corsRules', parameters('corsRules')), null())]", - "defaultServiceVersion": "[if(not(empty(parameters('defaultServiceVersion'))), parameters('defaultServiceVersion'), null())]", - "deleteRetentionPolicy": { - "enabled": "[parameters('deleteRetentionPolicyEnabled')]", - "days": "[parameters('deleteRetentionPolicyDays')]", - "allowPermanentDelete": "[if(and(parameters('deleteRetentionPolicyEnabled'), parameters('deleteRetentionPolicyAllowPermanentDelete')), true(), null())]" - }, - "isVersioningEnabled": "[parameters('isVersioningEnabled')]", - "lastAccessTimeTrackingPolicy": "[if(not(equals(reference('storageAccount', '2022-09-01', 'full').kind, 'Storage')), createObject('enable', parameters('lastAccessTimeTrackingPolicyEnabled'), 'name', if(equals(parameters('lastAccessTimeTrackingPolicyEnabled'), true()), 'AccessTimeTracking', null()), 'trackingGranularityInDays', if(equals(parameters('lastAccessTimeTrackingPolicyEnabled'), true()), 1, null())), null())]", - "restorePolicy": "[if(parameters('restorePolicyEnabled'), createObject('enabled', true(), 'days', parameters('restorePolicyDays')), null())]" - }, - "dependsOn": [ - "storageAccount" - ] + "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" + } + } + } + } }, - "blobServices_diagnosticSettings": { - "copy": { - "name": "blobServices_diagnosticSettings", - "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" - }, - "type": "Microsoft.Insights/diagnosticSettings", - "apiVersion": "2021-05-01-preview", - "scope": "[format('Microsoft.Storage/storageAccounts/{0}/blobServices/{1}', parameters('storageAccountName'), variables('name'))]", - "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', variables('name')))]", + "privateEndpoint": { + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-11-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", "properties": { "copy": [ { - "name": "metrics", - "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", - "input": { - "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", - "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", - "timeGrain": null - } - }, - { - "name": "logs", - "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "name": "applicationSecurityGroups", + "count": "[length(coalesce(parameters('applicationSecurityGroupResourceIds'), createArray()))]", "input": { - "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", - "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", - "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + "id": "[coalesce(parameters('applicationSecurityGroupResourceIds'), createArray())[copyIndex('applicationSecurityGroups')]]" } } ], - "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", - "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", - "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", - "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", - "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", - "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + "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": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" }, "dependsOn": [ - "blobServices" + "privateEndpoint" ] }, - "blobServices_container": { + "privateEndpoint_roleAssignments": { "copy": { - "name": "blobServices_container", - "count": "[length(coalesce(parameters('containers'), createArray()))]" + "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": "2022-09-01", - "name": "[format('{0}-Container-{1}', deployment().name, copyIndex())]", + "name": "[format('{0}-PrivateEndpoint-PrivateDnsZoneGroup', uniqueString(deployment().name))]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "storageAccountName": { - "value": "[parameters('storageAccountName')]" - }, - "blobServiceName": { - "value": "[variables('name')]" - }, "name": { - "value": "[coalesce(parameters('containers'), createArray())[copyIndex()].name]" - }, - "defaultEncryptionScope": { - "value": "[tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'defaultEncryptionScope')]" - }, - "denyEncryptionScopeOverride": { - "value": "[tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'denyEncryptionScopeOverride')]" - }, - "enableNfsV3AllSquash": { - "value": "[tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'enableNfsV3AllSquash')]" - }, - "enableNfsV3RootSquash": { - "value": "[tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'enableNfsV3RootSquash')]" - }, - "immutableStorageWithVersioningEnabled": { - "value": "[tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'immutableStorageWithVersioningEnabled')]" - }, - "metadata": { - "value": "[tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'metadata')]" - }, - "publicAccess": { - "value": "[tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'publicAccess')]" + "value": "[tryGet(parameters('privateDnsZoneGroup'), 'name')]" }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'roleAssignments')]" + "privateEndpointName": { + "value": "[parameters('name')]" }, - "immutabilityPolicyProperties": { - "value": "[tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'immutabilityPolicyProperties')]" + "privateDnsZoneConfigs": { + "value": "[parameters('privateDnsZoneGroup').privateDnsZoneGroupConfigs]" } }, "template": { @@ -27468,397 +23024,109 @@ "_generator": { "name": "bicep", "version": "0.33.13.18514", - "templateHash": "10816586207828434096" + "templateHash": "5440815542537978381" }, - "name": "Storage Account Blob Containers", - "description": "This module deploys a Storage Account Blob Container." + "name": "Private Endpoint Private DNS Zone Groups", + "description": "This module deploys a Private Endpoint Private DNS Zone Group." }, "definitions": { - "roleAssignmentType": { + "privateDnsZoneGroupConfigType": { "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." + "description": "Optional. The name of the private DNS zone group config." } }, - "delegatedManagedIdentityResourceId": { + "privateDnsZoneResourceId": { "type": "string", - "nullable": true, "metadata": { - "description": "Optional. The Resource Id of the delegated managed identity resource." + "description": "Required. The resource id of the private DNS zone." } } }, "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.5.1" - } + "__bicep_export!": true } } }, "parameters": { - "storageAccountName": { + "privateEndpointName": { "type": "string", - "maxLength": 24, "metadata": { - "description": "Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment." + "description": "Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment." } }, - "blobServiceName": { - "type": "string", - "defaultValue": "default", + "privateDnsZoneConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, + "minLength": 1, + "maxLength": 5, "metadata": { - "description": "Optional. The name of the parent Blob Service. Required if the template is used in a standalone deployment." + "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": "Required. The name of the storage container to deploy." - } - }, - "defaultEncryptionScope": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Optional. Default the container to use specified encryption scope for all writes." + "description": "Optional. The name of the private DNS zone group." } - }, - "denyEncryptionScopeOverride": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "Optional. Block override of encryption scope from the container default." + } + }, + "variables": { + "copy": [ + { + "name": "privateDnsZoneConfigsVar", + "count": "[length(parameters('privateDnsZoneConfigs'))]", + "input": { + "name": "[coalesce(tryGet(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')], 'name'), last(split(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId, '/')))]", + "properties": { + "privateDnsZoneId": "[parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId]" + } + } } - }, - "enableNfsV3AllSquash": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "Optional. Enable NFSv3 all squash on blob container." - } - }, - "enableNfsV3RootSquash": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "Optional. Enable NFSv3 root squash on blob container." - } - }, - "immutableStorageWithVersioningEnabled": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "Optional. This is an immutable property, when set to true it enables object level immutability at the container level. The property is immutable and can only be set to true at the container creation time. Existing containers must undergo a migration process." - } - }, - "immutabilityPolicyName": { - "type": "string", - "defaultValue": "default", - "metadata": { - "description": "Optional. Name of the immutable policy." - } - }, - "immutabilityPolicyProperties": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Configure immutability policy." - } - }, - "metadata": { - "type": "object", - "defaultValue": {}, - "metadata": { - "description": "Optional. A name-value pair to associate with the container as metadata." - } - }, - "publicAccess": { - "type": "string", - "defaultValue": "None", - "allowedValues": [ - "Container", - "Blob", - "None" - ], - "metadata": { - "description": "Optional. Specifies whether data in the container may be accessed publicly and the level of access." - } - }, - "roleAssignments": { - "type": "array", - "items": { - "$ref": "#/definitions/roleAssignmentType" - }, - "nullable": true, - "metadata": { - "description": "Optional. Array of role assignments to create." - } - } - }, - "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')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Reader and Data Access": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c12c1c16-33a1-487b-954d-41c89c60f349')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "Storage Account Backup Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e5e2a7ff-d759-4cd2-bb51-3152d37e2eb1')]", - "Storage Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab')]", - "Storage Account Key Operator Service Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '81a9662b-bebf-436f-a333-f67b29880f12')]", - "Storage Blob Data Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe')]", - "Storage Blob Data Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b7e6dc6d-f1e8-4753-8033-0f276bb0955b')]", - "Storage Blob Data Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2a2b9908-6ea1-4ae2-8e65-a410df84e7d1')]", - "Storage Blob Delegator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'db58b8e5-c6ad-4a2a-8342-4190687cbf4a')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" - } + ] }, "resources": { - "storageAccount::blobServices": { - "existing": true, - "type": "Microsoft.Storage/storageAccounts/blobServices", - "apiVersion": "2022-09-01", - "name": "[format('{0}/{1}', parameters('storageAccountName'), parameters('blobServiceName'))]" - }, - "storageAccount": { + "privateEndpoint": { "existing": true, - "type": "Microsoft.Storage/storageAccounts", - "apiVersion": "2022-09-01", - "name": "[parameters('storageAccountName')]" + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-11-01", + "name": "[parameters('privateEndpointName')]" }, - "container": { - "type": "Microsoft.Storage/storageAccounts/blobServices/containers", - "apiVersion": "2022-09-01", - "name": "[format('{0}/{1}/{2}', parameters('storageAccountName'), parameters('blobServiceName'), parameters('name'))]", + "privateDnsZoneGroup": { + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2023-11-01", + "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", "properties": { - "defaultEncryptionScope": "[if(not(empty(parameters('defaultEncryptionScope'))), parameters('defaultEncryptionScope'), null())]", - "denyEncryptionScopeOverride": "[if(equals(parameters('denyEncryptionScopeOverride'), true()), parameters('denyEncryptionScopeOverride'), null())]", - "enableNfsV3AllSquash": "[if(equals(parameters('enableNfsV3AllSquash'), true()), parameters('enableNfsV3AllSquash'), null())]", - "enableNfsV3RootSquash": "[if(equals(parameters('enableNfsV3RootSquash'), true()), parameters('enableNfsV3RootSquash'), null())]", - "immutableStorageWithVersioning": "[if(equals(parameters('immutableStorageWithVersioningEnabled'), true()), createObject('enabled', parameters('immutableStorageWithVersioningEnabled')), null())]", - "metadata": "[parameters('metadata')]", - "publicAccess": "[parameters('publicAccess')]" + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" } - }, - "container_roleAssignments": { - "copy": { - "name": "container_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Storage/storageAccounts/{0}/blobServices/{1}/containers/{2}', parameters('storageAccountName'), parameters('blobServiceName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Storage/storageAccounts/blobServices/containers', parameters('storageAccountName'), parameters('blobServiceName'), 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": [ - "container" - ] - }, - "immutabilityPolicy": { - "condition": "[not(empty(coalesce(parameters('immutabilityPolicyProperties'), createObject())))]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[parameters('immutabilityPolicyName')]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "storageAccountName": { - "value": "[parameters('storageAccountName')]" - }, - "containerName": { - "value": "[parameters('name')]" - }, - "immutabilityPeriodSinceCreationInDays": { - "value": "[tryGet(parameters('immutabilityPolicyProperties'), 'immutabilityPeriodSinceCreationInDays')]" - }, - "allowProtectedAppendWrites": { - "value": "[tryGet(parameters('immutabilityPolicyProperties'), 'allowProtectedAppendWrites')]" - }, - "allowProtectedAppendWritesAll": { - "value": "[tryGet(parameters('immutabilityPolicyProperties'), 'allowProtectedAppendWritesAll')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "2769922037435749045" - }, - "name": "Storage Account Blob Container Immutability Policies", - "description": "This module deploys a Storage Account Blob Container Immutability Policy." - }, - "parameters": { - "storageAccountName": { - "type": "string", - "maxLength": 24, - "metadata": { - "description": "Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment." - } - }, - "containerName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent container to apply the policy to. Required if the template is used in a standalone deployment." - } - }, - "immutabilityPeriodSinceCreationInDays": { - "type": "int", - "defaultValue": 365, - "metadata": { - "description": "Optional. The immutability period for the blobs in the container since the policy creation, in days." - } - }, - "allowProtectedAppendWrites": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Optional. This property can only be changed for unlocked time-based retention policies. When enabled, new blocks can be written to an append blob while maintaining immutability protection and compliance. Only new blocks can be added and any existing blocks cannot be modified or deleted. This property cannot be changed with ExtendImmutabilityPolicy API." - } - }, - "allowProtectedAppendWritesAll": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Optional. This property can only be changed for unlocked time-based retention policies. When enabled, new blocks can be written to both \"Append and Block Blobs\" while maintaining immutability protection and compliance. Only new blocks can be added and any existing blocks cannot be modified or deleted. This property cannot be changed with ExtendImmutabilityPolicy API. The \"allowProtectedAppendWrites\" and \"allowProtectedAppendWritesAll\" properties are mutually exclusive." - } - } - }, - "resources": [ - { - "type": "Microsoft.Storage/storageAccounts/blobServices/containers/immutabilityPolicies", - "apiVersion": "2022-09-01", - "name": "[format('{0}/{1}/{2}/{3}', parameters('storageAccountName'), 'default', parameters('containerName'), 'default')]", - "properties": { - "immutabilityPeriodSinceCreationInDays": "[parameters('immutabilityPeriodSinceCreationInDays')]", - "allowProtectedAppendWrites": "[parameters('allowProtectedAppendWrites')]", - "allowProtectedAppendWritesAll": "[parameters('allowProtectedAppendWritesAll')]" - } - } - ], - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the deployed immutability policy." - }, - "value": "default" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the deployed immutability policy." - }, - "value": "[resourceId('Microsoft.Storage/storageAccounts/blobServices/containers/immutabilityPolicies', parameters('storageAccountName'), 'default', parameters('containerName'), 'default')]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group of the deployed immutability policy." - }, - "value": "[resourceGroup().name]" - } - } - } - }, - "dependsOn": [ - "container" - ] } }, "outputs": { "name": { "type": "string", "metadata": { - "description": "The name of the deployed container." + "description": "The name of the private endpoint DNS zone group." }, "value": "[parameters('name')]" }, "resourceId": { "type": "string", "metadata": { - "description": "The resource ID of the deployed container." + "description": "The resource ID of the private endpoint DNS zone group." }, - "value": "[resourceId('Microsoft.Storage/storageAccounts/blobServices/containers', parameters('storageAccountName'), parameters('blobServiceName'), parameters('name'))]" + "value": "[resourceId('Microsoft.Network/privateEndpoints/privateDnsZoneGroups', parameters('privateEndpointName'), parameters('name'))]" }, "resourceGroupName": { "type": "string", "metadata": { - "description": "The resource group of the deployed container." + "description": "The resource group the private endpoint DNS zone group was deployed into." }, "value": "[resourceGroup().name]" } @@ -27866,1413 +23134,1459 @@ } }, "dependsOn": [ - "blobServices" + "privateEndpoint" ] } }, "outputs": { - "name": { + "resourceGroupName": { "type": "string", "metadata": { - "description": "The name of the deployed blob service." + "description": "The resource group the private endpoint was deployed into." }, - "value": "[variables('name')]" + "value": "[resourceGroup().name]" }, "resourceId": { "type": "string", "metadata": { - "description": "The resource ID of the deployed blob service." + "description": "The resource ID of the private endpoint." }, - "value": "[resourceId('Microsoft.Storage/storageAccounts/blobServices', parameters('storageAccountName'), variables('name'))]" + "value": "[resourceId('Microsoft.Network/privateEndpoints', parameters('name'))]" }, - "resourceGroupName": { + "name": { "type": "string", "metadata": { - "description": "The name of the deployed blob service." + "description": "The name of the private endpoint." }, - "value": "[resourceGroup().name]" + "value": "[parameters('name')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('privateEndpoint', '2023-11-01', 'full').location]" + }, + "customDnsConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/customDnsConfigType" + }, + "metadata": { + "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": [ - "storageAccount" + "registry", + "registry_replications" ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The Name of the Azure container registry." + }, + "value": "[parameters('name')]" }, - "storageAccount_fileServices": { - "condition": "[not(empty(parameters('fileServices')))]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-Storage-FileServices', uniqueString(deployment().name, parameters('location')))]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "storageAccountName": { - "value": "[parameters('name')]" - }, - "diagnosticSettings": { - "value": "[tryGet(parameters('fileServices'), 'diagnosticSettings')]" - }, - "protocolSettings": { - "value": "[tryGet(parameters('fileServices'), 'protocolSettings')]" - }, - "shareDeleteRetentionPolicy": { - "value": "[tryGet(parameters('fileServices'), 'shareDeleteRetentionPolicy')]" - }, - "shares": { - "value": "[tryGet(parameters('fileServices'), 'shares')]" - }, - "corsRules": { - "value": "[tryGet(parameters('queueServices'), 'corsRules')]" - } - }, - "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.33.13.18514", - "templateHash": "987643333058038389" - }, - "name": "Storage Account File Share Services", - "description": "This module deploys a Storage Account File Share Service." - }, - "definitions": { - "corsRuleType": { - "type": "object", - "properties": { - "allowedHeaders": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. A list of headers allowed to be part of the cross-origin request." - } - }, - "allowedMethods": { - "type": "array", - "allowedValues": [ - "CONNECT", - "DELETE", - "GET", - "HEAD", - "MERGE", - "OPTIONS", - "PATCH", - "POST", - "PUT", - "TRACE" - ], - "metadata": { - "description": "Required. A list of HTTP methods that are allowed to be executed by the origin." - } - }, - "allowedOrigins": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. A list of origin domains that will be allowed via CORS, or \"*\" to allow all domains." - } - }, - "exposedHeaders": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. A list of response headers to expose to CORS clients." - } - }, - "maxAgeInSeconds": { - "type": "int", - "metadata": { - "description": "Required. The number of seconds that the client/browser should cache a preflight response." - } - } - }, - "metadata": { - "__bicep_export!": true, - "description": "The type for a cors rule." - } - }, - "diagnosticSettingFullType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the diagnostic setting." - } - }, - "logCategoriesAndGroups": { - "type": "array", - "items": { - "type": "object", - "properties": { - "category": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." - } - }, - "categoryGroup": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." - } - }, - "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 logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." - } - }, - "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": { + "loginServer": { + "type": "string", + "metadata": { + "description": "The reference to the Azure container registry." + }, + "value": "[reference(resourceId('Microsoft.ContainerRegistry/registries', parameters('name')), '2019-05-01').loginServer]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the Azure container registry." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the Azure container registry." + }, + "value": "[resourceId('Microsoft.ContainerRegistry/registries', parameters('name'))]" + }, + "systemAssignedMIPrincipalId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "The principal ID of the system assigned identity." + }, + "value": "[tryGet(tryGet(reference('registry', '2023-06-01-preview', 'full'), 'identity'), 'principalId')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('registry', '2023-06-01-preview', 'full').location]" + }, + "credentialSetsSystemAssignedMIPrincipalIds": { + "type": "array", + "metadata": { + "description": "The Principal IDs of the ACR Credential Sets system-assigned identities." + }, + "copy": { + "count": "[length(range(0, length(coalesce(parameters('credentialSets'), createArray()))))]", + "input": "[tryGet(tryGet(reference(format('registry_credentialSets[{0}]', range(0, length(coalesce(parameters('credentialSets'), createArray())))[copyIndex()])).outputs, 'systemAssignedMIPrincipalId'), 'value')]" + } + }, + "credentialSetsResourceIds": { + "type": "array", + "metadata": { + "description": "The Resource IDs of the ACR Credential Sets." + }, + "copy": { + "count": "[length(range(0, length(coalesce(parameters('credentialSets'), createArray()))))]", + "input": "[reference(format('registry_credentialSets[{0}]', range(0, length(coalesce(parameters('credentialSets'), createArray())))[copyIndex()])).outputs.resourceId.value]" + } + }, + "privateEndpoints": { + "type": "array", + "items": { + "$ref": "#/definitions/privateEndpointOutputType" + }, + "metadata": { + "description": "The private endpoints of the Azure container registry." + }, + "copy": { + "count": "[length(if(not(empty(parameters('privateEndpoints'))), array(parameters('privateEndpoints')), createArray()))]", + "input": { + "name": "[reference(format('registry_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", + "resourceId": "[reference(format('registry_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", + "groupId": "[tryGet(tryGet(reference(format('registry_privateEndpoints[{0}]', copyIndex())).outputs, 'groupId'), 'value')]", + "customDnsConfigs": "[reference(format('registry_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfigs.value]", + "networkInterfaceResourceIds": "[reference(format('registry_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceResourceIds.value]" + } + } + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'private-dns-acr-deployment')]" + ] + } + ], + "outputs": { + "resourceId": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', take(format('{0}-container-registry-deployment', variables('nameFormatted')), 64)), '2022-09-01').outputs.resourceId.value]" + }, + "loginServer": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', take(format('{0}-container-registry-deployment', variables('nameFormatted')), 64)), '2022-09-01').outputs.loginServer.value]" + }, + "name": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', take(format('{0}-container-registry-deployment', variables('nameFormatted')), 64)), '2022-09-01').outputs.name.value]" + } + } + } + }, + "dependsOn": [ + "logAnalyticsWorkspace", + "network" + ] + }, + "storageAccount": { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('{0}-storage-account-deployment', parameters('name')), 64)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "storageName": { + "value": "[format('st{0}{1}', parameters('name'), variables('resourceToken'))]" + }, + "location": { + "value": "[parameters('location')]" + }, + "networkIsolation": { + "value": "[parameters('networkIsolation')]" + }, + "virtualNetworkResourceId": "[if(parameters('networkIsolation'), createObject('value', reference('network').outputs.resourceId.value), createObject('value', ''))]", + "virtualNetworkSubnetResourceId": "[if(parameters('networkIsolation'), createObject('value', reference('network').outputs.defaultSubnetResourceId.value), createObject('value', ''))]", + "logAnalyticsWorkspaceResourceId": { + "value": "[reference('logAnalyticsWorkspace').outputs.resourceId.value]" + }, + "roleAssignments": { + "value": "[concat(if(empty(parameters('userObjectId')), createArray(), createArray(createObject('principalId', parameters('userObjectId'), 'principalType', 'User', 'roleDefinitionIdOrName', 'Storage Blob Data Contributor'))), createArray(createObject('principalId', reference('cognitiveServices').outputs.aiServicesSystemAssignedMIPrincipalId.value, 'principalType', 'ServicePrincipal', 'roleDefinitionIdOrName', 'Storage Blob Data Contributor')), if(parameters('searchEnabled'), createArray(createObject('principalId', if(parameters('searchEnabled'), reference('aiSearch').outputs.systemAssignedMIPrincipalId.value, ''), 'principalType', 'ServicePrincipal', 'roleDefinitionIdOrName', 'Storage Blob Data Contributor')), createArray()))]" + }, + "tags": { + "value": "[variables('allTags')]" + } + }, + "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.1.42791", + "templateHash": "17337127158083160925" + } + }, + "definitions": { + "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.5.1" + } + } + } + }, + "parameters": { + "storageName": { + "type": "string", + "metadata": { + "description": "Name of the Storage Account." + } + }, + "location": { + "type": "string", + "metadata": { + "description": "Specifies the location for all the Azure resources." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Tags to be applied to the resources." + } + }, + "virtualNetworkResourceId": { + "type": "string", + "metadata": { + "description": "Resource ID of the virtual network to link the private DNS zones." + } + }, + "virtualNetworkSubnetResourceId": { + "type": "string", + "metadata": { + "description": "Resource ID of the subnet for the private endpoint." + } + }, + "logAnalyticsWorkspaceResourceId": { + "type": "string", + "metadata": { + "description": "Resource ID of the Log Analytics workspace to use for diagnostic settings." + } + }, + "networkIsolation": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Specifies whether network isolation is enabled. This will create a private endpoint for the Storage Account and link the private DNS zone." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "nameFormatted": "[take(toLower(parameters('storageName')), 24)]" + }, + "resources": { + "blobPrivateDnsZone": { + "condition": "[parameters('networkIsolation')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "private-dns-blob-deployment", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[format('privatelink.blob.{0}', environment().suffixes.storage)]" + }, + "virtualNetworkLinks": { + "value": [ + { + "virtualNetworkResourceId": "[parameters('virtualNetworkResourceId')]" + } + ] + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "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.32.4.45862", + "templateHash": "83178825086050429" + }, + "name": "Private DNS Zones", + "description": "This module deploys a Private DNS zone.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "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." + } + } + } + }, + "nullable": true + }, + "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." + } + } + }, + "nullable": true + }, + "aType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "aRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "ipv4Address": { "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 IPv4 address of this A record." } } - }, - "metadata": { - "description": "An AVM-aligned type for a diagnostic setting. To be used if both logs & metrics are supported by the resource provider.", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } - } - } - }, - "parameters": { - "storageAccountName": { - "type": "string", - "maxLength": 24, - "metadata": { - "description": "Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment." } }, - "name": { - "type": "string", - "defaultValue": "default", - "metadata": { - "description": "Optional. The name of the file service." - } - }, - "protocolSettings": { - "type": "object", - "defaultValue": {}, - "metadata": { - "description": "Optional. Protocol settings for file service." - } - }, - "shareDeleteRetentionPolicy": { + "nullable": true, + "metadata": { + "description": "Optional. The list of A records in the record set." + } + } + } + }, + "nullable": true + }, + "aaaaType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "aaaaRecords": { + "type": "array", + "items": { "type": "object", - "defaultValue": { - "enabled": true, - "days": 7 - }, - "metadata": { - "description": "Optional. The service properties for soft delete." + "properties": { + "ipv6Address": { + "type": "string", + "metadata": { + "description": "Required. The IPv6 address of this AAAA record." + } + } } }, - "corsRules": { - "type": "array", - "items": { - "$ref": "#/definitions/corsRuleType" - }, - "nullable": true, - "metadata": { - "description": "Optional. The List of CORS rules. You can include up to five CorsRule elements in the request." + "nullable": true, + "metadata": { + "description": "Optional. The list of AAAA records in the record set." + } + } + } + }, + "nullable": true + }, + "cnameType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "cnameRecord": { + "type": "object", + "properties": { + "cname": { + "type": "string", + "metadata": { + "description": "Required. The canonical name of the CNAME record." + } } }, - "diagnosticSettings": { - "type": "array", - "items": { - "$ref": "#/definitions/diagnosticSettingFullType" - }, - "nullable": true, - "metadata": { - "description": "Optional. The diagnostic settings of the service." + "nullable": true, + "metadata": { + "description": "Optional. The CNAME record in the record set." + } + } + } + }, + "nullable": true + }, + "mxType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "mxRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "exchange": { + "type": "string", + "metadata": { + "description": "Required. The domain name of the mail host for this MX record." + } + }, + "preference": { + "type": "int", + "metadata": { + "description": "Required. The preference value for this MX record." + } + } } }, - "shares": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. File shares to create." - } + "nullable": true, + "metadata": { + "description": "Optional. The list of MX records in the record set." + } + } + } + }, + "nullable": true + }, + "ptrType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." } }, - "resources": { - "storageAccount": { - "existing": true, - "type": "Microsoft.Storage/storageAccounts", - "apiVersion": "2023-04-01", - "name": "[parameters('storageAccountName')]" - }, - "fileServices": { - "type": "Microsoft.Storage/storageAccounts/fileServices", - "apiVersion": "2023-04-01", - "name": "[format('{0}/{1}', parameters('storageAccountName'), parameters('name'))]", + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "ptrRecords": { + "type": "array", + "items": { + "type": "object", "properties": { - "cors": "[if(not(equals(parameters('corsRules'), null())), createObject('corsRules', parameters('corsRules')), null())]", - "protocolSettings": "[parameters('protocolSettings')]", - "shareDeleteRetentionPolicy": "[parameters('shareDeleteRetentionPolicy')]" + "ptrdname": { + "type": "string", + "metadata": { + "description": "Required. The PTR target domain name for this PTR record." + } + } } }, - "fileServices_diagnosticSettings": { - "copy": { - "name": "fileServices_diagnosticSettings", - "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + "nullable": true, + "metadata": { + "description": "Optional. The list of PTR records in the record set." + } + } + } + }, + "nullable": true + }, + "soaType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "soaRecord": { + "type": "object", + "properties": { + "email": { + "type": "string", + "metadata": { + "description": "Required. The email contact for this SOA record." + } }, - "type": "Microsoft.Insights/diagnosticSettings", - "apiVersion": "2021-05-01-preview", - "scope": "[format('Microsoft.Storage/storageAccounts/{0}/fileServices/{1}', parameters('storageAccountName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", - "properties": { - "copy": [ - { - "name": "metrics", - "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", - "input": { - "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", - "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", - "timeGrain": null - } - }, - { - "name": "logs", - "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", - "input": { - "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", - "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", - "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" - } - } - ], - "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", - "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", - "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", - "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", - "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", - "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + "expireTime": { + "type": "int", + "metadata": { + "description": "Required. The expire time for this SOA record." + } }, - "dependsOn": [ - "fileServices" - ] - }, - "fileServices_shares": { - "copy": { - "name": "fileServices_shares", - "count": "[length(coalesce(parameters('shares'), createArray()))]" + "host": { + "type": "string", + "metadata": { + "description": "Required. The domain name of the authoritative name server for this SOA record." + } }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-shares-{1}', deployment().name, copyIndex())]", + "minimumTtl": { + "type": "int", + "metadata": { + "description": "Required. The minimum value for this SOA record. By convention this is used to determine the negative caching duration." + } + }, + "refreshTime": { + "type": "int", + "metadata": { + "description": "Required. The refresh value for this SOA record." + } + }, + "retryTime": { + "type": "int", + "metadata": { + "description": "Required. The retry time for this SOA record." + } + }, + "serialNumber": { + "type": "int", + "metadata": { + "description": "Required. The serial number for this SOA record." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The SOA record in the record set." + } + } + } + }, + "nullable": true + }, + "srvType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "srvRecords": { + "type": "array", + "items": { + "type": "object", "properties": { - "expressionEvaluationOptions": { - "scope": "inner" + "priority": { + "type": "int", + "metadata": { + "description": "Required. The priority value for this SRV record." + } }, - "mode": "Incremental", - "parameters": { - "storageAccountName": { - "value": "[parameters('storageAccountName')]" - }, - "fileServicesName": { - "value": "[parameters('name')]" - }, - "name": { - "value": "[coalesce(parameters('shares'), createArray())[copyIndex()].name]" - }, - "accessTier": { - "value": "[coalesce(tryGet(coalesce(parameters('shares'), createArray())[copyIndex()], 'accessTier'), if(equals(reference('storageAccount', '2023-04-01', 'full').kind, 'FileStorage'), 'Premium', 'TransactionOptimized'))]" - }, - "enabledProtocols": { - "value": "[tryGet(coalesce(parameters('shares'), createArray())[copyIndex()], 'enabledProtocols')]" - }, - "rootSquash": { - "value": "[tryGet(coalesce(parameters('shares'), createArray())[copyIndex()], 'rootSquash')]" - }, - "shareQuota": { - "value": "[tryGet(coalesce(parameters('shares'), createArray())[copyIndex()], 'shareQuota')]" - }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('shares'), createArray())[copyIndex()], 'roleAssignments')]" + "weight": { + "type": "int", + "metadata": { + "description": "Required. The weight value for this SRV record." } }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "languageVersion": "2.0", - "contentVersion": "1.0.0.0", + "port": { + "type": "int", "metadata": { - "_generator": { - "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "15193761941438215308" - }, - "name": "Storage Account File Shares", - "description": "This module deploys a Storage Account File Share." - }, - "definitions": { - "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.5.1" - } - } - } - }, - "parameters": { - "storageAccountName": { - "type": "string", - "maxLength": 24, - "metadata": { - "description": "Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment." - } - }, - "fileServicesName": { - "type": "string", - "defaultValue": "default", - "metadata": { - "description": "Conditional. The name of the parent file service. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the file share to create." - } - }, - "accessTier": { - "type": "string", - "defaultValue": "TransactionOptimized", - "allowedValues": [ - "Premium", - "Hot", - "Cool", - "TransactionOptimized" - ], - "metadata": { - "description": "Conditional. Access tier for specific share. Required if the Storage Account kind is set to FileStorage (should be set to \"Premium\"). GpV2 account can choose between TransactionOptimized (default), Hot, and Cool." - } - }, - "shareQuota": { - "type": "int", - "defaultValue": 5120, - "metadata": { - "description": "Optional. The maximum size of the share, in gigabytes. Must be greater than 0, and less than or equal to 5120 (5TB). For Large File Shares, the maximum size is 102400 (100TB)." - } - }, - "enabledProtocols": { - "type": "string", - "defaultValue": "SMB", - "allowedValues": [ - "NFS", - "SMB" - ], - "metadata": { - "description": "Optional. The authentication protocol that is used for the file share. Can only be specified when creating a share." - } - }, - "rootSquash": { - "type": "string", - "defaultValue": "NoRootSquash", - "allowedValues": [ - "AllSquash", - "NoRootSquash", - "RootSquash" - ], - "metadata": { - "description": "Optional. Permissions for NFS file shares are enforced by the client OS rather than the Azure Files service. Toggling the root squash behavior reduces the rights of the root user for NFS shares." - } - }, - "roleAssignments": { - "type": "array", - "items": { - "$ref": "#/definitions/roleAssignmentType" - }, - "nullable": true, - "metadata": { - "description": "Optional. Array of role assignments to create." - } - } - }, - "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')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Reader and Data Access": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c12c1c16-33a1-487b-954d-41c89c60f349')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "Storage Account Backup Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e5e2a7ff-d759-4cd2-bb51-3152d37e2eb1')]", - "Storage Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab')]", - "Storage Account Key Operator Service Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '81a9662b-bebf-436f-a333-f67b29880f12')]", - "Storage File Data SMB Share Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0c867c2a-1d8c-454a-a3db-ab2ea1bdc8bb')]", - "Storage File Data SMB Share Elevated Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a7264617-510b-434b-a828-9731dc254ea7')]", - "Storage File Data SMB Share Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aba4ae5f-2193-4029-9191-0cb91df5e314')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" - } - }, - "resources": { - "storageAccount::fileService": { - "existing": true, - "type": "Microsoft.Storage/storageAccounts/fileServices", - "apiVersion": "2023-04-01", - "name": "[format('{0}/{1}', parameters('storageAccountName'), parameters('fileServicesName'))]" - }, - "storageAccount": { - "existing": true, - "type": "Microsoft.Storage/storageAccounts", - "apiVersion": "2023-04-01", - "name": "[parameters('storageAccountName')]" - }, - "fileShare": { - "type": "Microsoft.Storage/storageAccounts/fileServices/shares", - "apiVersion": "2023-01-01", - "name": "[format('{0}/{1}/{2}', parameters('storageAccountName'), parameters('fileServicesName'), parameters('name'))]", - "properties": { - "accessTier": "[parameters('accessTier')]", - "shareQuota": "[parameters('shareQuota')]", - "rootSquash": "[if(equals(parameters('enabledProtocols'), 'NFS'), parameters('rootSquash'), null())]", - "enabledProtocols": "[parameters('enabledProtocols')]" - } - }, - "fileShare_roleAssignments": { - "copy": { - "name": "fileShare_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-Share-Rbac-{1}', uniqueString(deployment().name), copyIndex())]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "scope": { - "value": "[replace(resourceId('Microsoft.Storage/storageAccounts/fileServices/shares', parameters('storageAccountName'), parameters('fileServicesName'), parameters('name')), '/shares/', '/fileshares/')]" - }, - "name": { - "value": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Storage/storageAccounts/fileServices/shares', parameters('storageAccountName'), parameters('fileServicesName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]" - }, - "roleDefinitionId": { - "value": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]" - }, - "principalId": { - "value": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]" - }, - "principalType": { - "value": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]" - }, - "condition": { - "value": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]" - }, - "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), createObject('value', coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0')), createObject('value', null()))]", - "delegatedManagedIdentityResourceId": { - "value": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" - }, - "description": { - "value": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "parameters": { - "scope": { - "type": "string", - "metadata": { - "description": "Required. The scope to deploy the role assignment to." - } - }, - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the role assignment." - } - }, - "roleDefinitionId": { - "type": "string", - "metadata": { - "description": "Required. The role definition Id to assign." - } - }, - "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", - "" - ], - "defaultValue": "", - "metadata": { - "description": "Optional. The principal type of the assigned principal ID." - } - }, - "description": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Optional. The description of the role assignment." - } - }, - "condition": { - "type": "string", - "defaultValue": "", - "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" - ], - "defaultValue": "2.0", - "metadata": { - "description": "Optional. Version of the condition." - } - }, - "delegatedManagedIdentityResourceId": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Optional. The Resource Id of the delegated managed identity resource." - } - } - }, - "resources": [ - { - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[parameters('scope')]", - "name": "[parameters('name')]", - "properties": { - "roleDefinitionId": "[parameters('roleDefinitionId')]", - "principalId": "[parameters('principalId')]", - "description": "[parameters('description')]", - "principalType": "[if(not(empty(parameters('principalType'))), parameters('principalType'), null())]", - "condition": "[if(not(empty(parameters('condition'))), parameters('condition'), null())]", - "conditionVersion": "[if(and(not(empty(parameters('conditionVersion'))), not(empty(parameters('condition')))), parameters('conditionVersion'), null())]", - "delegatedManagedIdentityResourceId": "[if(not(empty(parameters('delegatedManagedIdentityResourceId'))), parameters('delegatedManagedIdentityResourceId'), null())]" - } - } - ] - } - }, - "dependsOn": [ - "fileShare" - ] - } - }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the deployed file share." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the deployed file share." - }, - "value": "[resourceId('Microsoft.Storage/storageAccounts/fileServices/shares', parameters('storageAccountName'), parameters('fileServicesName'), parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group of the deployed file share." - }, - "value": "[resourceGroup().name]" - } + "description": "Required. The port value for this SRV record." + } + }, + "target": { + "type": "string", + "metadata": { + "description": "Required. The target domain name for this SRV record." } } - }, - "dependsOn": [ - "fileServices", - "storageAccount" - ] - } - }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the deployed file share service." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the deployed file share service." - }, - "value": "[resourceId('Microsoft.Storage/storageAccounts/fileServices', parameters('storageAccountName'), parameters('name'))]" + } }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group of the deployed file share service." - }, - "value": "[resourceGroup().name]" + "nullable": true, + "metadata": { + "description": "Optional. The list of SRV records in the record set." } } } }, - "dependsOn": [ - "storageAccount" - ] + "nullable": true }, - "storageAccount_queueServices": { - "condition": "[not(empty(parameters('queueServices')))]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-Storage-QueueServices', uniqueString(deployment().name, parameters('location')))]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "storageAccountName": { - "value": "[parameters('name')]" + "txtType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } }, - "diagnosticSettings": { - "value": "[tryGet(parameters('queueServices'), 'diagnosticSettings')]" + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } }, - "queues": { - "value": "[tryGet(parameters('queueServices'), 'queues')]" + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } }, - "corsRules": { - "value": "[tryGet(parameters('queueServices'), 'corsRules')]" - } - }, - "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.33.13.18514", - "templateHash": "8158577333548255612" - }, - "name": "Storage Account Queue Services", - "description": "This module deploys a Storage Account Queue Service." + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } }, - "definitions": { - "corsRuleType": { + "txtRecords": { + "type": "array", + "items": { "type": "object", "properties": { - "allowedHeaders": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. A list of headers allowed to be part of the cross-origin request." - } - }, - "allowedMethods": { - "type": "array", - "allowedValues": [ - "CONNECT", - "DELETE", - "GET", - "HEAD", - "MERGE", - "OPTIONS", - "PATCH", - "POST", - "PUT", - "TRACE" - ], - "metadata": { - "description": "Required. A list of HTTP methods that are allowed to be executed by the origin." - } - }, - "allowedOrigins": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. A list of origin domains that will be allowed via CORS, or \"*\" to allow all domains." - } - }, - "exposedHeaders": { + "value": { "type": "array", "items": { "type": "string" }, "metadata": { - "description": "Required. A list of response headers to expose to CORS clients." - } - }, - "maxAgeInSeconds": { - "type": "int", - "metadata": { - "description": "Required. The number of seconds that the client/browser should cache a preflight response." + "description": "Required. The text value of this TXT record." } } - }, - "metadata": { - "__bicep_export!": true, - "description": "The type for a cors rule." } }, - "diagnosticSettingFullType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the diagnostic setting." - } - }, - "logCategoriesAndGroups": { - "type": "array", - "items": { - "type": "object", - "properties": { - "category": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." - } - }, - "categoryGroup": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." - } - }, - "enabled": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Enable or disable the category explicitly. Default is `true`." - } - } + "nullable": true, + "metadata": { + "description": "Optional. The list of TXT records in the record set." + } + } + } + }, + "nullable": true + }, + "virtualNetworkLinkType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "minLength": 1, + "maxLength": 80, + "metadata": { + "description": "Optional. The resource name." + } + }, + "virtualNetworkResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the virtual network to link." + } + }, + "location": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Azure Region where the resource lives." + } + }, + "registrationEnabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Is auto-registration of virtual machine records in the virtual network in the Private DNS zone enabled?." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Resource tags." + } + }, + "resolutionPolicy": { + "type": "string", + "allowedValues": [ + "Default", + "NxDomainRedirect" + ], + "nullable": true, + "metadata": { + "description": "Optional. The resolution type of the private-dns-zone fallback machanism." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Private DNS zone name." + } + }, + "a": { + "$ref": "#/definitions/aType", + "metadata": { + "description": "Optional. Array of A records." + } + }, + "aaaa": { + "$ref": "#/definitions/aaaaType", + "metadata": { + "description": "Optional. Array of AAAA records." + } + }, + "cname": { + "$ref": "#/definitions/cnameType", + "metadata": { + "description": "Optional. Array of CNAME records." + } + }, + "mx": { + "$ref": "#/definitions/mxType", + "metadata": { + "description": "Optional. Array of MX records." + } + }, + "ptr": { + "$ref": "#/definitions/ptrType", + "metadata": { + "description": "Optional. Array of PTR records." + } + }, + "soa": { + "$ref": "#/definitions/soaType", + "metadata": { + "description": "Optional. Array of SOA records." + } + }, + "srv": { + "$ref": "#/definitions/srvType", + "metadata": { + "description": "Optional. Array of SRV records." + } + }, + "txt": { + "$ref": "#/definitions/txtType", + "metadata": { + "description": "Optional. Array of TXT records." + } + }, + "virtualNetworkLinks": { + "$ref": "#/definitions/virtualNetworkLinkType", + "metadata": { + "description": "Optional. Array of custom objects describing vNet links of the DNS zone. Each object should contain properties 'virtualNetworkResourceId' and 'registrationEnabled'. The 'vnetResourceId' is a resource ID of a vNet to link, 'registrationEnabled' (bool) enables automatic DNS registration in the zone for the linked vNet." + } + }, + "location": { + "type": "string", + "defaultValue": "global", + "metadata": { + "description": "Optional. The location of the PrivateDNSZone. Should be global." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "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')]", + "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": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privatednszone.{0}.{1}', replace('0.7.0', '.', '-'), 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" + } + } + } + } + }, + "privateDnsZone": { + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]" + }, + "privateDnsZone_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/privateDnsZones/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "privateDnsZone_roleAssignments": { + "copy": { + "name": "privateDnsZone_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones', 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": [ + "privateDnsZone" + ] + }, + "privateDnsZone_A": { + "copy": { + "name": "privateDnsZone_A", + "count": "[length(coalesce(parameters('a'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-ARecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('a'), createArray())[copyIndex()].name]" + }, + "aRecords": { + "value": "[tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'aRecords')]" + }, + "metadata": { + "value": "[tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'metadata')]" + }, + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'ttl'), 3600)]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'roleAssignments')]" + } + }, + "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.32.4.45862", + "templateHash": "2531120132215940282" + }, + "name": "Private DNS Zone A record", + "description": "This module deploys a Private DNS Zone A record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "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." } }, - "nullable": true, - "metadata": { - "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." - } - }, - "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`." - } - } + "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'." } }, - "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." + "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 diagnostic setting. To be used if both logs & metrics are supported by the resource provider.", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } - } + "nullable": true } }, "parameters": { - "storageAccountName": { + "privateDnsZoneName": { "type": "string", - "maxLength": 24, "metadata": { - "description": "Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment." + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." } }, - "queues": { - "type": "array", - "nullable": true, + "name": { + "type": "string", "metadata": { - "description": "Optional. Queues to create." + "description": "Required. The name of the A record." } }, - "corsRules": { + "aRecords": { "type": "array", - "items": { - "$ref": "#/definitions/corsRuleType" - }, "nullable": true, "metadata": { - "description": "Optional. The List of CORS rules. You can include up to five CorsRule elements in the request." + "description": "Optional. The list of A records in the record set." } }, - "diagnosticSettings": { - "type": "array", - "items": { - "$ref": "#/definitions/diagnosticSettingFullType" - }, + "metadata": { + "type": "object", "nullable": true, "metadata": { - "description": "Optional. The diagnostic settings of the service." + "description": "Optional. The metadata attached to the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." } } }, "variables": { - "name": "default" + "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')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } }, "resources": { - "storageAccount": { + "privateDnsZone": { "existing": true, - "type": "Microsoft.Storage/storageAccounts", - "apiVersion": "2023-04-01", - "name": "[parameters('storageAccountName')]" + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" }, - "queueServices": { - "type": "Microsoft.Storage/storageAccounts/queueServices", - "apiVersion": "2023-04-01", - "name": "[format('{0}/{1}', parameters('storageAccountName'), variables('name'))]", + "A": { + "type": "Microsoft.Network/privateDnsZones/A", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", "properties": { - "cors": "[if(not(equals(parameters('corsRules'), null())), createObject('corsRules', parameters('corsRules')), null())]" + "aRecords": "[parameters('aRecords')]", + "metadata": "[parameters('metadata')]", + "ttl": "[parameters('ttl')]" } }, - "queueServices_diagnosticSettings": { + "A_roleAssignments": { "copy": { - "name": "queueServices_diagnosticSettings", - "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + "name": "A_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, - "type": "Microsoft.Insights/diagnosticSettings", - "apiVersion": "2021-05-01-preview", - "scope": "[format('Microsoft.Storage/storageAccounts/{0}/queueServices/{1}', parameters('storageAccountName'), variables('name'))]", - "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', variables('name')))]", + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/A/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/A', parameters('privateDnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "copy": [ - { - "name": "metrics", - "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", - "input": { - "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", - "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", - "timeGrain": null - } - }, - { - "name": "logs", - "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", - "input": { - "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", - "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", - "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" - } - } - ], - "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", - "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", - "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", - "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", - "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", - "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + "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": [ - "queueServices" + "A" ] - }, - "queueServices_queues": { - "copy": { - "name": "queueServices_queues", - "count": "[length(coalesce(parameters('queues'), createArray()))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-Queue-{1}', deployment().name, copyIndex())]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "storageAccountName": { - "value": "[parameters('storageAccountName')]" - }, - "name": { - "value": "[coalesce(parameters('queues'), createArray())[copyIndex()].name]" - }, - "metadata": { - "value": "[tryGet(coalesce(parameters('queues'), createArray())[copyIndex()], 'metadata')]" - }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('queues'), createArray())[copyIndex()], 'roleAssignments')]" - } - }, - "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.33.13.18514", - "templateHash": "9877120144610775153" - }, - "name": "Storage Account Queues", - "description": "This module deploys a Storage Account Queue." - }, - "definitions": { - "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.5.1" - } - } - } - }, - "parameters": { - "storageAccountName": { - "type": "string", - "maxLength": 24, - "metadata": { - "description": "Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the storage queue to deploy." - } - }, - "metadata": { - "type": "object", - "defaultValue": {}, - "metadata": { - "description": "Optional. A name-value pair that represents queue metadata." - } - }, - "roleAssignments": { - "type": "array", - "items": { - "$ref": "#/definitions/roleAssignmentType" - }, - "nullable": true, - "metadata": { - "description": "Optional. Array of role assignments to create." - } - } - }, - "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')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Reader and Data Access": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c12c1c16-33a1-487b-954d-41c89c60f349')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "Storage Account Backup Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e5e2a7ff-d759-4cd2-bb51-3152d37e2eb1')]", - "Storage Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab')]", - "Storage Account Key Operator Service Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '81a9662b-bebf-436f-a333-f67b29880f12')]", - "Storage Queue Data Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '974c5e8b-45b9-4653-ba55-5f855dd0fb88')]", - "Storage Queue Data Message Processor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8a0f0c08-91a1-4084-bc3d-661d67233fed')]", - "Storage Queue Data Message Sender": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c6a89b2d-59bc-44d0-9896-0f6e12d7b80a')]", - "Storage Queue Data Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '19e7f393-937e-4f77-808e-94535e297925')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" - } - }, - "resources": { - "storageAccount::queueServices": { - "existing": true, - "type": "Microsoft.Storage/storageAccounts/queueServices", - "apiVersion": "2023-04-01", - "name": "[format('{0}/{1}', parameters('storageAccountName'), 'default')]" - }, - "storageAccount": { - "existing": true, - "type": "Microsoft.Storage/storageAccounts", - "apiVersion": "2023-04-01", - "name": "[parameters('storageAccountName')]" - }, - "queue": { - "type": "Microsoft.Storage/storageAccounts/queueServices/queues", - "apiVersion": "2023-04-01", - "name": "[format('{0}/{1}/{2}', parameters('storageAccountName'), 'default', parameters('name'))]", - "properties": { - "metadata": "[parameters('metadata')]" - } - }, - "queue_roleAssignments": { - "copy": { - "name": "queue_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Storage/storageAccounts/{0}/queueServices/{1}/queues/{2}', parameters('storageAccountName'), 'default', parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Storage/storageAccounts/queueServices/queues', parameters('storageAccountName'), 'default', 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": [ - "queue" - ] - } - }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the deployed queue." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the deployed queue." - }, - "value": "[resourceId('Microsoft.Storage/storageAccounts/queueServices/queues', parameters('storageAccountName'), 'default', parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group of the deployed queue." - }, - "value": "[resourceGroup().name]" - } - } - } - } } }, "outputs": { "name": { "type": "string", "metadata": { - "description": "The name of the deployed file share service." + "description": "The name of the deployed A record." }, - "value": "[variables('name')]" + "value": "[parameters('name')]" }, "resourceId": { "type": "string", "metadata": { - "description": "The resource ID of the deployed file share service." + "description": "The resource ID of the deployed A record." }, - "value": "[resourceId('Microsoft.Storage/storageAccounts/queueServices', parameters('storageAccountName'), variables('name'))]" + "value": "[resourceId('Microsoft.Network/privateDnsZones/A', parameters('privateDnsZoneName'), parameters('name'))]" }, "resourceGroupName": { "type": "string", "metadata": { - "description": "The resource group of the deployed file share service." + "description": "The resource group of the deployed A record." }, "value": "[resourceGroup().name]" } @@ -29280,31 +24594,40 @@ } }, "dependsOn": [ - "storageAccount" + "privateDnsZone" ] }, - "storageAccount_tableServices": { - "condition": "[not(empty(parameters('tableServices')))]", + "privateDnsZone_AAAA": { + "copy": { + "name": "privateDnsZone_AAAA", + "count": "[length(coalesce(parameters('aaaa'), createArray()))]" + }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-Storage-TableServices', uniqueString(deployment().name, parameters('location')))]", + "name": "[format('{0}-PrivateDnsZone-AAAARecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "storageAccountName": { + "privateDnsZoneName": { "value": "[parameters('name')]" }, - "diagnosticSettings": { - "value": "[tryGet(parameters('tableServices'), 'diagnosticSettings')]" + "name": { + "value": "[coalesce(parameters('aaaa'), createArray())[copyIndex()].name]" }, - "tables": { - "value": "[tryGet(parameters('tableServices'), 'tables')]" + "aaaaRecords": { + "value": "[tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'aaaaRecords')]" }, - "corsRules": { - "value": "[tryGet(parameters('tableServices'), 'corsRules')]" + "metadata": { + "value": "[tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'metadata')]" + }, + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'ttl'), 3600)]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'roleAssignments')]" } }, "template": { @@ -29314,539 +24637,206 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "541986423744885003" + "version": "0.32.4.45862", + "templateHash": "16709340450244912125" }, - "name": "Storage Account Table Services", - "description": "This module deploys a Storage Account Table Service." + "name": "Private DNS Zone AAAA record", + "description": "This module deploys a Private DNS Zone AAAA record.", + "owner": "Azure/module-maintainers" }, "definitions": { - "corsRuleType": { - "type": "object", - "properties": { - "allowedHeaders": { - "type": "array", - "items": { - "type": "string" + "roleAssignmentType": { + "type": "array", + "items": { + "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." + } }, - "metadata": { - "description": "Required. A list of headers allowed to be part of the cross-origin request." - } - }, - "allowedMethods": { - "type": "array", - "allowedValues": [ - "CONNECT", - "DELETE", - "GET", - "HEAD", - "MERGE", - "OPTIONS", - "PATCH", - "POST", - "PUT", - "TRACE" - ], - "metadata": { - "description": "Required. A list of HTTP methods that are allowed to be executed by the origin." - } - }, - "allowedOrigins": { - "type": "array", - "items": { - "type": "string" + "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'." + } }, - "metadata": { - "description": "Required. A list of origin domains that will be allowed via CORS, or \"*\" to allow all domains." - } - }, - "exposedHeaders": { - "type": "array", - "items": { - "type": "string" + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } }, - "metadata": { - "description": "Required. A list of response headers to expose to CORS clients." - } - }, - "maxAgeInSeconds": { - "type": "int", - "metadata": { - "description": "Required. The number of seconds that the client/browser should cache a preflight response." + "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." + } } } }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", "metadata": { - "__bicep_export!": true, - "description": "The type for a cors rule." + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." } }, - "diagnosticSettingFullType": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the AAAA record." + } + }, + "aaaaRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of AAAA records in the record set." + } + }, + "metadata": { "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the diagnostic setting." - } - }, - "logCategoriesAndGroups": { - "type": "array", - "items": { - "type": "object", - "properties": { - "category": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." - } - }, - "categoryGroup": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." - } - }, - "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 logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." - } - }, - "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 both logs & metrics are supported by the resource provider.", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } - } - } - }, - "parameters": { - "storageAccountName": { - "type": "string", - "maxLength": 24, - "metadata": { - "description": "Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment." - } - }, - "tables": { - "type": "array", - "defaultValue": [], + "nullable": true, "metadata": { - "description": "Optional. tables to create." + "description": "Optional. The metadata attached to the record set." } }, - "corsRules": { - "type": "array", - "items": { - "$ref": "#/definitions/corsRuleType" - }, - "nullable": true, + "ttl": { + "type": "int", + "defaultValue": 3600, "metadata": { - "description": "Optional. The List of CORS rules. You can include up to five CorsRule elements in the request." + "description": "Optional. The TTL (time-to-live) of the records in the record set." } }, - "diagnosticSettings": { - "type": "array", - "items": { - "$ref": "#/definitions/diagnosticSettingFullType" - }, - "nullable": true, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", "metadata": { - "description": "Optional. The diagnostic settings of the service." + "description": "Optional. Array of role assignments to create." } } }, "variables": { - "name": "default" + "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')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } }, "resources": { - "storageAccount": { + "privateDnsZone": { "existing": true, - "type": "Microsoft.Storage/storageAccounts", - "apiVersion": "2023-04-01", - "name": "[parameters('storageAccountName')]" + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" }, - "tableServices": { - "type": "Microsoft.Storage/storageAccounts/tableServices", - "apiVersion": "2023-04-01", - "name": "[format('{0}/{1}', parameters('storageAccountName'), variables('name'))]", + "AAAA": { + "type": "Microsoft.Network/privateDnsZones/AAAA", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", "properties": { - "cors": "[if(not(equals(parameters('corsRules'), null())), createObject('corsRules', parameters('corsRules')), null())]" + "aaaaRecords": "[parameters('aaaaRecords')]", + "metadata": "[parameters('metadata')]", + "ttl": "[parameters('ttl')]" } }, - "tableServices_diagnosticSettings": { + "AAAA_roleAssignments": { "copy": { - "name": "tableServices_diagnosticSettings", - "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + "name": "AAAA_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, - "type": "Microsoft.Insights/diagnosticSettings", - "apiVersion": "2021-05-01-preview", - "scope": "[format('Microsoft.Storage/storageAccounts/{0}/tableServices/{1}', parameters('storageAccountName'), variables('name'))]", - "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', variables('name')))]", + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/AAAA/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/AAAA', parameters('privateDnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "copy": [ - { - "name": "metrics", - "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", - "input": { - "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", - "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", - "timeGrain": null - } - }, - { - "name": "logs", - "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", - "input": { - "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", - "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", - "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" - } - } - ], - "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", - "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", - "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", - "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", - "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", - "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + "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": [ - "tableServices" + "AAAA" ] - }, - "tableServices_tables": { - "copy": { - "name": "tableServices_tables", - "count": "[length(parameters('tables'))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-Table-{1}', deployment().name, copyIndex())]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[parameters('tables')[copyIndex()].name]" - }, - "storageAccountName": { - "value": "[parameters('storageAccountName')]" - }, - "roleAssignments": { - "value": "[tryGet(parameters('tables')[copyIndex()], 'roleAssignments')]" - } - }, - "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.33.13.18514", - "templateHash": "11234204519679347949" - }, - "name": "Storage Account Table", - "description": "This module deploys a Storage Account Table." - }, - "definitions": { - "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.5.1" - } - } - } - }, - "parameters": { - "storageAccountName": { - "type": "string", - "maxLength": 24, - "metadata": { - "description": "Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment." - } - }, - "roleAssignments": { - "type": "array", - "items": { - "$ref": "#/definitions/roleAssignmentType" - }, - "nullable": true, - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "name": { - "type": "string", - "metadata": { - "description": "Required. Name of the table." - } - } - }, - "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')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Reader and Data Access": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c12c1c16-33a1-487b-954d-41c89c60f349')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "Storage Account Backup Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e5e2a7ff-d759-4cd2-bb51-3152d37e2eb1')]", - "Storage Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab')]", - "Storage Account Key Operator Service Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '81a9662b-bebf-436f-a333-f67b29880f12')]", - "Storage Table Data Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3')]", - "Storage Table Data Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '76199698-9eea-4c19-bc75-cec21354c6b6')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" - } - }, - "resources": { - "storageAccount::tableServices": { - "existing": true, - "type": "Microsoft.Storage/storageAccounts/tableServices", - "apiVersion": "2023-04-01", - "name": "[format('{0}/{1}', parameters('storageAccountName'), 'default')]" - }, - "storageAccount": { - "existing": true, - "type": "Microsoft.Storage/storageAccounts", - "apiVersion": "2023-04-01", - "name": "[parameters('storageAccountName')]" - }, - "table": { - "type": "Microsoft.Storage/storageAccounts/tableServices/tables", - "apiVersion": "2023-04-01", - "name": "[format('{0}/{1}/{2}', parameters('storageAccountName'), 'default', parameters('name'))]" - }, - "table_roleAssignments": { - "copy": { - "name": "table_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Storage/storageAccounts/{0}/tableServices/{1}/tables/{2}', parameters('storageAccountName'), 'default', parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Storage/storageAccounts/tableServices/tables', parameters('storageAccountName'), 'default', 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": [ - "table" - ] - } - }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the deployed file share service." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the deployed file share service." - }, - "value": "[resourceId('Microsoft.Storage/storageAccounts/tableServices/tables', parameters('storageAccountName'), 'default', parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group of the deployed file share service." - }, - "value": "[resourceGroup().name]" - } - } - } - } } }, "outputs": { "name": { "type": "string", "metadata": { - "description": "The name of the deployed table service." + "description": "The name of the deployed AAAA record." }, - "value": "[variables('name')]" + "value": "[parameters('name')]" }, "resourceId": { "type": "string", "metadata": { - "description": "The resource ID of the deployed table service." + "description": "The resource ID of the deployed AAAA record." }, - "value": "[resourceId('Microsoft.Storage/storageAccounts/tableServices', parameters('storageAccountName'), variables('name'))]" + "value": "[resourceId('Microsoft.Network/privateDnsZones/AAAA', parameters('privateDnsZoneName'), parameters('name'))]" }, "resourceGroupName": { "type": "string", "metadata": { - "description": "The resource group of the deployed table service." + "description": "The resource group of the deployed AAAA record." }, "value": "[resourceGroup().name]" } @@ -29854,27 +24844,40 @@ } }, "dependsOn": [ - "storageAccount" + "privateDnsZone" ] }, - "secretsExport": { - "condition": "[not(equals(parameters('secretsExportConfiguration'), null()))]", + "privateDnsZone_CNAME": { + "copy": { + "name": "privateDnsZone_CNAME", + "count": "[length(coalesce(parameters('cname'), createArray()))]" + }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-secrets-kv', uniqueString(deployment().name, parameters('location')))]", - "subscriptionId": "[split(coalesce(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '//'), '/')[2]]", - "resourceGroup": "[split(coalesce(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '////'), '/')[4]]", + "name": "[format('{0}-PrivateDnsZone-CNAMERecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "keyVaultName": { - "value": "[last(split(coalesce(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '//'), '/'))]" + "privateDnsZoneName": { + "value": "[parameters('name')]" }, - "secretsToSet": { - "value": "[union(createArray(), if(contains(parameters('secretsExportConfiguration'), 'accessKey1Name'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'accessKey1Name'), 'value', listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('name')), '2023-05-01').keys[0].value)), createArray()), if(contains(parameters('secretsExportConfiguration'), 'connectionString1Name'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'connectionString1Name'), 'value', format('DefaultEndpointsProtocol=https;AccountName={0};AccountKey={1};EndpointSuffix={2}', parameters('name'), listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('name')), '2023-05-01').keys[0].value, environment().suffixes.storage))), createArray()), if(contains(parameters('secretsExportConfiguration'), 'accessKey2Name'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'accessKey2Name'), 'value', listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('name')), '2023-05-01').keys[1].value)), createArray()), if(contains(parameters('secretsExportConfiguration'), 'connectionString2Name'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'connectionString2Name'), 'value', format('DefaultEndpointsProtocol=https;AccountName={0};AccountKey={1};EndpointSuffix={2}', parameters('name'), listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('name')), '2023-05-01').keys[1].value, environment().suffixes.storage))), createArray()))]" + "name": { + "value": "[coalesce(parameters('cname'), createArray())[copyIndex()].name]" + }, + "cnameRecord": { + "value": "[tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'cnameRecord')]" + }, + "metadata": { + "value": "[tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'metadata')]" + }, + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'ttl'), 3600)]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'roleAssignments')]" } }, "template": { @@ -29884,1404 +24887,1247 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "14510275109257916717" - } + "version": "0.32.4.45862", + "templateHash": "9976020649752073181" + }, + "name": "Private DNS Zone CNAME record", + "description": "This module deploys a Private DNS Zone CNAME record.", + "owner": "Azure/module-maintainers" }, "definitions": { - "secretSetOutputType": { - "type": "object", - "properties": { - "secretResourceId": { - "type": "string", - "metadata": { - "description": "The resourceId of the exported secret." - } - }, - "secretUri": { - "type": "string", - "metadata": { - "description": "The secret URI of the exported secret." - } - }, - "secretUriWithVersion": { - "type": "string", - "metadata": { - "description": "The secret URI with version of the exported secret." + "roleAssignmentType": { + "type": "array", + "items": { + "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." + } } } }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", "metadata": { - "description": "An AVM-aligned type for the output of the secret set via the secrets export feature.", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." } }, - "secretToSetType": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the CNAME record." + } + }, + "cnameRecord": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. A CNAME record." + } + }, + "metadata": { "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "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')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "CNAME": { + "type": "Microsoft.Network/privateDnsZones/CNAME", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the secret to set." - } - }, - "value": { - "type": "securestring", - "metadata": { - "description": "Required. The value of the secret to set." - } - } + "cnameRecord": "[parameters('cnameRecord')]", + "metadata": "[parameters('metadata')]", + "ttl": "[parameters('ttl')]" + } + }, + "CNAME_roleAssignments": { + "copy": { + "name": "CNAME_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/CNAME/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/CNAME', parameters('privateDnsZoneName'), 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": [ + "CNAME" + ] + } + }, + "outputs": { + "name": { + "type": "string", "metadata": { - "description": "An AVM-aligned type for the secret to set via the secrets export feature.", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + "description": "The name of the deployed CNAME record." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed CNAME record." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/CNAME', parameters('privateDnsZoneName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed CNAME record." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "privateDnsZone_MX": { + "copy": { + "name": "privateDnsZone_MX", + "count": "[length(coalesce(parameters('mx'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-MXRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('mx'), createArray())[copyIndex()].name]" + }, + "metadata": { + "value": "[tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'metadata')]" + }, + "mxRecords": { + "value": "[tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'mxRecords')]" + }, + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'ttl'), 3600)]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'roleAssignments')]" + } + }, + "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.32.4.45862", + "templateHash": "2520323624213076361" + }, + "name": "Private DNS Zone MX record", + "description": "This module deploys a Private DNS Zone MX record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "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." + } + } } - } + }, + "nullable": true } }, "parameters": { - "keyVaultName": { + "privateDnsZoneName": { "type": "string", "metadata": { - "description": "Required. The name of the Key Vault to set the ecrets in." + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." } }, - "secretsToSet": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the MX record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "mxRecords": { "type": "array", - "items": { - "$ref": "#/definitions/secretToSetType" - }, + "nullable": true, "metadata": { - "description": "Required. The secrets to set in the Key Vault." + "description": "Optional. The list of MX records in the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "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')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, "resources": { - "keyVault": { + "privateDnsZone": { "existing": true, - "type": "Microsoft.KeyVault/vaults", - "apiVersion": "2022-07-01", - "name": "[parameters('keyVaultName')]" + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" }, - "secrets": { + "MX": { + "type": "Microsoft.Network/privateDnsZones/MX", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "properties": { + "metadata": "[parameters('metadata')]", + "mxRecords": "[parameters('mxRecords')]", + "ttl": "[parameters('ttl')]" + } + }, + "MX_roleAssignments": { "copy": { - "name": "secrets", - "count": "[length(parameters('secretsToSet'))]" + "name": "MX_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, - "type": "Microsoft.KeyVault/vaults/secrets", - "apiVersion": "2023-07-01", - "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('secretsToSet')[copyIndex()].name)]", + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/MX/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/MX', parameters('privateDnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "value": "[parameters('secretsToSet')[copyIndex()].value]" - } + "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": [ + "MX" + ] } }, "outputs": { - "secretsSet": { - "type": "array", - "items": { - "$ref": "#/definitions/secretSetOutputType" + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed MX record." }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", "metadata": { - "description": "The references to the secrets exported to the provided Key Vault." + "description": "The resource ID of the deployed MX record." }, - "copy": { - "count": "[length(range(0, length(coalesce(parameters('secretsToSet'), createArray()))))]", - "input": { - "secretResourceId": "[resourceId('Microsoft.KeyVault/vaults/secrets', parameters('keyVaultName'), parameters('secretsToSet')[range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()]].name)]", - "secretUri": "[reference(format('secrets[{0}]', range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()])).secretUri]", - "secretUriWithVersion": "[reference(format('secrets[{0}]', range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()])).secretUriWithVersion]" - } - } + "value": "[resourceId('Microsoft.Network/privateDnsZones/MX', parameters('privateDnsZoneName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed MX record." + }, + "value": "[resourceGroup().name]" } } } }, "dependsOn": [ - "storageAccount" + "privateDnsZone" ] - } - }, - "outputs": { - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the deployed storage account." - }, - "value": "[resourceId('Microsoft.Storage/storageAccounts', parameters('name'))]" - }, - "name": { - "type": "string", - "metadata": { - "description": "The name of the deployed storage account." - }, - "value": "[parameters('name')]" }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group of the deployed storage account." - }, - "value": "[resourceGroup().name]" - }, - "primaryBlobEndpoint": { - "type": "string", - "metadata": { - "description": "The primary blob endpoint reference if blob services are deployed." - }, - "value": "[if(and(not(empty(parameters('blobServices'))), contains(parameters('blobServices'), 'containers')), reference(format('Microsoft.Storage/storageAccounts/{0}', parameters('name')), '2019-04-01').primaryEndpoints.blob, '')]" - }, - "systemAssignedMIPrincipalId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "The principal ID of the system assigned identity." - }, - "value": "[tryGet(tryGet(reference('storageAccount', '2023-05-01', 'full'), 'identity'), 'principalId')]" - }, - "location": { - "type": "string", - "metadata": { - "description": "The location the resource was deployed into." + "privateDnsZone_PTR": { + "copy": { + "name": "privateDnsZone_PTR", + "count": "[length(coalesce(parameters('ptr'), createArray()))]" }, - "value": "[reference('storageAccount', '2023-05-01', 'full').location]" - }, - "serviceEndpoints": { - "type": "object", - "metadata": { - "description": "All service endpoints of the deployed storage account, Note Standard_LRS and Standard_ZRS accounts only have a blob service endpoint." - }, - "value": "[reference('storageAccount').primaryEndpoints]" - }, - "privateEndpoints": { - "type": "array", - "items": { - "$ref": "#/definitions/privateEndpointOutputType" - }, - "metadata": { - "description": "The private endpoints of the Storage Account." - }, - "copy": { - "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]", - "input": { - "name": "[reference(format('storageAccount_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", - "resourceId": "[reference(format('storageAccount_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", - "groupId": "[tryGet(tryGet(reference(format('storageAccount_privateEndpoints[{0}]', copyIndex())).outputs, 'groupId'), 'value')]", - "customDnsConfigs": "[reference(format('storageAccount_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfigs.value]", - "networkInterfaceResourceIds": "[reference(format('storageAccount_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceResourceIds.value]" - } - } - }, - "exportedSecrets": { - "$ref": "#/definitions/secretsOutputType", - "metadata": { - "description": "A hashtable of references to the secrets exported to the provided Key Vault. The key of each reference is each secret's name." - }, - "value": "[if(not(equals(parameters('secretsExportConfiguration'), null())), toObject(reference('secretsExport').outputs.secretsSet.value, lambda('secret', last(split(lambdaVariables('secret').secretResourceId, '/'))), lambda('secret', lambdaVariables('secret'))), createObject())]" - } - } - } - }, - "dependsOn": [ - "blobPrivateDnsZone", - "filePrivateDnsZone" - ] - } - }, - "outputs": { - "storageName": { - "type": "string", - "value": "[reference('storageAccount').outputs.name.value]" - }, - "storageResourceId": { - "type": "string", - "value": "[reference('storageAccount').outputs.resourceId.value]" - } - } - } - }, - "dependsOn": [ - "aiSearch", - "cognitiveServices", - "logAnalyticsWorkspace", - "network" - ] - }, - "cognitiveServices": { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-cognitive-services-deployment', parameters('name'))]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[parameters('name')]" - }, - "resourceToken": { - "value": "[variables('resourceToken')]" - }, - "location": { - "value": "[parameters('aiDeploymentsLocation')]" - }, - "networkIsolation": { - "value": "[parameters('networkIsolation')]" - }, - "networkAcls": { - "value": "[parameters('networkAcls')]" - }, - "virtualNetworkResourceId": "[if(parameters('networkIsolation'), createObject('value', reference('network').outputs.virtualNetworkId.value), createObject('value', ''))]", - "virtualNetworkSubnetResourceId": "[if(parameters('networkIsolation'), createObject('value', reference('network').outputs.vmSubnetId.value), createObject('value', ''))]", - "logAnalyticsWorkspaceResourceId": { - "value": "[reference('logAnalyticsWorkspace').outputs.resourceId.value]" - }, - "aiModelDeployments": { - "copy": [ - { - "name": "value", - "count": "[length(createArray(parameters('aiEmbeddingModelDeployment'), parameters('aiGPTModelDeployment')))]", - "input": "[createObject('name', if(empty(tryGet(createArray(parameters('aiEmbeddingModelDeployment'), parameters('aiGPTModelDeployment'))[copyIndex('value')], 'name')), createArray(parameters('aiEmbeddingModelDeployment'), parameters('aiGPTModelDeployment'))[copyIndex('value')].modelName, tryGet(createArray(parameters('aiEmbeddingModelDeployment'), parameters('aiGPTModelDeployment'))[copyIndex('value')], 'name')), 'model', createObject('name', createArray(parameters('aiEmbeddingModelDeployment'), parameters('aiGPTModelDeployment'))[copyIndex('value')].modelName, 'format', 'OpenAI', 'version', createArray(parameters('aiEmbeddingModelDeployment'), parameters('aiGPTModelDeployment'))[copyIndex('value')].version), 'sku', createObject('name', 'GlobalStandard', 'capacity', createArray(parameters('aiEmbeddingModelDeployment'), parameters('aiGPTModelDeployment'))[copyIndex('value')].capacity))]" - } - ] - }, - "userObjectId": { - "value": "[parameters('userObjectId')]" - }, - "contentSafetyEnabled": { - "value": "[parameters('contentSafetyEnabled')]" - }, - "visionEnabled": { - "value": "[parameters('visionEnabled')]" - }, - "languageEnabled": { - "value": "[parameters('languageEnabled')]" - }, - "speechEnabled": { - "value": "[parameters('speechEnabled')]" - }, - "translatorEnabled": { - "value": "[parameters('translatorEnabled')]" - }, - "documentIntelligenceEnabled": { - "value": "[parameters('documentIntelligenceEnabled')]" - }, - "tags": { - "value": "[variables('allTags')]" - } - }, - "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.1.42791", - "templateHash": "532244891627057375" - } - }, - "definitions": { - "deploymentsType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Specify the name of cognitive service account deployment." - } - }, - "model": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of Cognitive Services account deployment model." - } - }, - "format": { - "type": "string", - "metadata": { - "description": "Required. The format of Cognitive Services account deployment model." - } - }, - "version": { - "type": "string", - "metadata": { - "description": "Required. The version of Cognitive Services account deployment model." - } - } - }, - "metadata": { - "description": "Required. Properties of Cognitive Services account deployment model." - } - }, - "sku": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the resource model definition representing SKU." - } - }, - "capacity": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The capacity of the resource model definition representing SKU." - } - }, - "tier": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The tier of the resource model definition representing SKU." - } - }, - "size": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The size of the resource model definition representing SKU." - } - }, - "family": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The family of the resource model definition representing SKU." - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. The resource model definition representing SKU." - } - }, - "raiPolicyName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of RAI policy." - } - }, - "versionUpgradeOption": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The version upgrade option." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "../customTypes.bicep" - } - } - } - }, - "parameters": { - "name": { - "type": "string", - "minLength": 3, - "maxLength": 12, - "metadata": { - "description": "Name of the Cognitive Services resource. Must be unique in the resource group." - } - }, - "resourceToken": { - "type": "string", - "metadata": { - "description": "Unique string to use when naming global resources." - } - }, - "location": { - "type": "string", - "metadata": { - "description": "Specifies the location for all the Azure resources. Defaults to the location of the resource group." - } - }, - "networkIsolation": { - "type": "bool", - "metadata": { - "description": "Specifies whether network isolation is enabled. When true, Foundry and related components will be deployed, network access parameters will be set to Disabled." - } - }, - "userObjectId": { - "type": "string", - "metadata": { - "description": "Specifies the object id of a Microsoft Entra ID user. In general, this the object id of the system administrator who deploys the Azure resources. This defaults to the deploying user." - } - }, - "tags": { - "type": "object", - "defaultValue": {}, - "metadata": { - "description": "Optional. Tags to be applied to the resources." - } - }, - "virtualNetworkResourceId": { - "type": "string", - "metadata": { - "description": "Resource ID of the virtual network to link the private DNS zones." - } - }, - "virtualNetworkSubnetResourceId": { - "type": "string", - "metadata": { - "description": "Resource ID of the subnet for the private endpoint." - } - }, - "logAnalyticsWorkspaceResourceId": { - "type": "string", - "metadata": { - "description": "Resource ID of the Log Analytics workspace to use for diagnostic settings." - } - }, - "aiModelDeployments": { - "type": "array", - "items": { - "$ref": "#/definitions/deploymentsType" - }, - "defaultValue": [], - "metadata": { - "description": "Optional. Specifies the OpenAI deployments to create." - } - }, - "contentSafetyEnabled": { - "type": "bool", - "metadata": { - "description": "Whether to include Azure AI Content Safety in the deployment." - } - }, - "visionEnabled": { - "type": "bool", - "metadata": { - "description": "Whether to include Azure AI Vision in the deployment." - } - }, - "languageEnabled": { - "type": "bool", - "metadata": { - "description": "Whether to include Azure AI Language in the deployment." - } - }, - "speechEnabled": { - "type": "bool", - "metadata": { - "description": "Whether to include Azure AI Speech in the deployment." - } - }, - "translatorEnabled": { - "type": "bool", - "metadata": { - "description": "Whether to include Azure AI Translator in the deployment." - } - }, - "documentIntelligenceEnabled": { - "type": "bool", - "metadata": { - "description": "Whether to include Azure Document Intelligence in the deployment." - } - }, - "networkAcls": { - "type": "object", - "metadata": { - "description": "Optional. A collection of rules governing the accessibility from specific network locations." - } - } - }, - "resources": { - "cognitiveServicesPrivateDnsZone": { - "condition": "[parameters('networkIsolation')]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "private-dns-cognitiveservices-deployment", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[format('privatelink.cognitiveservices.{0}', if(equals(toLower(environment().name), 'azureusgovernment'), 'azure.us', 'azure.com'))]" - }, - "virtualNetworkLinks": { - "value": [ - { - "virtualNetworkResourceId": "[parameters('virtualNetworkResourceId')]" - } - ] - }, - "tags": { - "value": "[parameters('tags')]" - } - }, - "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.32.4.45862", - "templateHash": "83178825086050429" - }, - "name": "Private DNS Zones", - "description": "This module deploys a Private DNS zone.", - "owner": "Azure/module-maintainers" - }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-PTRRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" }, - "principalType": { - "type": "string", - "allowedValues": [ - "Device", - "ForeignGroup", - "Group", - "ServicePrincipal", - "User" - ], - "nullable": true, - "metadata": { - "description": "Optional. The principal type of the assigned principal ID." - } + "name": { + "value": "[coalesce(parameters('ptr'), createArray())[copyIndex()].name]" }, - "description": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The description of the role assignment." - } + "metadata": { + "value": "[tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'metadata')]" }, - "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\"." - } + "ptrRecords": { + "value": "[tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'ptrRecords')]" }, - "conditionVersion": { - "type": "string", - "allowedValues": [ - "2.0" - ], - "nullable": true, - "metadata": { - "description": "Optional. Version of the condition." - } + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'ttl'), 3600)]" }, - "delegatedManagedIdentityResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The Resource Id of the delegated managed identity resource." - } - } - } - }, - "nullable": true - }, - "lockType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Specify the name of lock." + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'roleAssignments')]" } }, - "kind": { - "type": "string", - "allowedValues": [ - "CanNotDelete", - "None", - "ReadOnly" - ], - "nullable": true, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", "metadata": { - "description": "Optional. Specify the type of lock." - } - } - }, - "nullable": true - }, - "aType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } + "_generator": { + "name": "bicep", + "version": "0.32.4.45862", + "templateHash": "3080404733048745471" + }, + "name": "Private DNS Zone PTR record", + "description": "This module deploys a Private DNS Zone PTR record.", + "owner": "Azure/module-maintainers" }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "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." + } + } + } + }, + "nullable": true } }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the PTR record." + } + }, "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "aRecords": { - "type": "array", - "items": { "type": "object", - "properties": { - "ipv4Address": { - "type": "string", - "metadata": { - "description": "Required. The IPv4 address of this A record." - } - } + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." } }, - "nullable": true, - "metadata": { - "description": "Optional. The list of A records in the record set." - } - } - } - }, - "nullable": true - }, - "aaaaType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } - }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." + "ptrRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of PTR records in the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } } }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." + "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')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, - "aaaaRecords": { - "type": "array", - "items": { - "type": "object", + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "PTR": { + "type": "Microsoft.Network/privateDnsZones/PTR", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", "properties": { - "ipv6Address": { - "type": "string", - "metadata": { - "description": "Required. The IPv6 address of this AAAA record." - } - } + "metadata": "[parameters('metadata')]", + "ptrRecords": "[parameters('ptrRecords')]", + "ttl": "[parameters('ttl')]" } }, - "nullable": true, - "metadata": { - "description": "Optional. The list of AAAA records in the record set." - } - } - } - }, - "nullable": true - }, - "cnameType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } - }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." + "PTR_roleAssignments": { + "copy": { + "name": "PTR_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/PTR/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/PTR', parameters('privateDnsZoneName'), 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": [ + "PTR" + ] } }, - "cnameRecord": { - "type": "object", - "properties": { - "cname": { - "type": "string", - "metadata": { - "description": "Required. The canonical name of the CNAME record." - } - } + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed PTR record." + }, + "value": "[parameters('name')]" }, - "nullable": true, - "metadata": { - "description": "Optional. The CNAME record in the record set." + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed PTR record." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/PTR', parameters('privateDnsZoneName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed PTR record." + }, + "value": "[resourceGroup().name]" } } } }, - "nullable": true + "dependsOn": [ + "privateDnsZone" + ] }, - "mxType": { - "type": "array", - "items": { - "type": "object", - "properties": { + "privateDnsZone_SOA": { + "copy": { + "name": "privateDnsZone_SOA", + "count": "[length(coalesce(parameters('soa'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-SOARecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } + "value": "[coalesce(parameters('soa'), createArray())[copyIndex()].name]" }, "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } + "value": "[tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'metadata')]" + }, + "soaRecord": { + "value": "[tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'soaRecord')]" }, "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } + "value": "[coalesce(tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'ttl'), 3600)]" }, "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } + "value": "[tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'roleAssignments')]" + } + }, + "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.32.4.45862", + "templateHash": "6653951445614700931" + }, + "name": "Private DNS Zone SOA record", + "description": "This module deploys a Private DNS Zone SOA record.", + "owner": "Azure/module-maintainers" }, - "mxRecords": { - "type": "array", - "items": { - "type": "object", - "properties": { - "exchange": { - "type": "string", - "metadata": { - "description": "Required. The domain name of the mail host for this MX record." - } - }, - "preference": { - "type": "int", - "metadata": { - "description": "Required. The preference value for this MX record." + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "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." + } } } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the SOA record." } }, - "nullable": true, "metadata": { - "description": "Optional. The list of MX records in the record set." - } - } - } - }, - "nullable": true - }, - "ptrType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } - }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "soaRecord": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. A SOA record." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } } }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." + "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')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, - "ptrRecords": { - "type": "array", - "items": { - "type": "object", + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "SOA": { + "type": "Microsoft.Network/privateDnsZones/SOA", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", "properties": { - "ptrdname": { - "type": "string", - "metadata": { - "description": "Required. The PTR target domain name for this PTR record." - } - } + "metadata": "[parameters('metadata')]", + "soaRecord": "[parameters('soaRecord')]", + "ttl": "[parameters('ttl')]" } }, - "nullable": true, - "metadata": { - "description": "Optional. The list of PTR records in the record set." + "SOA_roleAssignments": { + "copy": { + "name": "SOA_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/SOA/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/SOA', parameters('privateDnsZoneName'), 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": [ + "SOA" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed SOA record." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed SOA record." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/SOA', parameters('privateDnsZoneName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed SOA record." + }, + "value": "[resourceGroup().name]" } } } }, - "nullable": true + "dependsOn": [ + "privateDnsZone" + ] }, - "soaType": { - "type": "array", - "items": { - "type": "object", - "properties": { + "privateDnsZone_SRV": { + "copy": { + "name": "privateDnsZone_SRV", + "count": "[length(coalesce(parameters('srv'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-SRVRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } + "value": "[coalesce(parameters('srv'), createArray())[copyIndex()].name]" }, "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } + "value": "[tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'metadata')]" + }, + "srvRecords": { + "value": "[tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'srvRecords')]" }, "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } + "value": "[coalesce(tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'ttl'), 3600)]" }, "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } + "value": "[tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'roleAssignments')]" + } + }, + "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.32.4.45862", + "templateHash": "5790774778713328446" + }, + "name": "Private DNS Zone SRV record", + "description": "This module deploys a Private DNS Zone SRV record.", + "owner": "Azure/module-maintainers" }, - "soaRecord": { - "type": "object", - "properties": { - "email": { - "type": "string", - "metadata": { - "description": "Required. The email contact for this SOA record." - } - }, - "expireTime": { - "type": "int", - "metadata": { - "description": "Required. The expire time for this SOA record." - } - }, - "host": { - "type": "string", - "metadata": { - "description": "Required. The domain name of the authoritative name server for this SOA record." - } - }, - "minimumTtl": { - "type": "int", - "metadata": { - "description": "Required. The minimum value for this SOA record. By convention this is used to determine the negative caching duration." - } - }, - "refreshTime": { - "type": "int", - "metadata": { - "description": "Required. The refresh value for this SOA record." - } - }, - "retryTime": { - "type": "int", - "metadata": { - "description": "Required. The retry time for this SOA record." + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "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." + } + } } }, - "serialNumber": { - "type": "int", - "metadata": { - "description": "Required. The serial number for this SOA record." - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. The SOA record in the record set." - } - } - } - }, - "nullable": true - }, - "srvType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." + "nullable": true } }, - "ttl": { - "type": "int", - "nullable": true, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the SRV record." + } + }, "metadata": { - "description": "Optional. The TTL of the record." + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "srvRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of SRV records in the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } } }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." + "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')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, - "srvRecords": { - "type": "array", - "items": { - "type": "object", + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "SRV": { + "type": "Microsoft.Network/privateDnsZones/SRV", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", "properties": { - "priority": { - "type": "int", - "metadata": { - "description": "Required. The priority value for this SRV record." - } - }, - "weight": { - "type": "int", - "metadata": { - "description": "Required. The weight value for this SRV record." - } - }, - "port": { - "type": "int", - "metadata": { - "description": "Required. The port value for this SRV record." - } - }, - "target": { - "type": "string", - "metadata": { - "description": "Required. The target domain name for this SRV record." - } - } + "metadata": "[parameters('metadata')]", + "srvRecords": "[parameters('srvRecords')]", + "ttl": "[parameters('ttl')]" } }, - "nullable": true, - "metadata": { - "description": "Optional. The list of SRV records in the record set." + "SRV_roleAssignments": { + "copy": { + "name": "SRV_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/SRV/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/SRV', parameters('privateDnsZoneName'), 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": [ + "SRV" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed SRV record." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed SRV record." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/SRV', parameters('privateDnsZoneName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed SRV record." + }, + "value": "[resourceGroup().name]" } } } }, - "nullable": true + "dependsOn": [ + "privateDnsZone" + ] }, - "txtType": { - "type": "array", - "items": { - "type": "object", - "properties": { + "privateDnsZone_TXT": { + "copy": { + "name": "privateDnsZone_TXT", + "count": "[length(coalesce(parameters('txt'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-TXTRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } + "value": "[coalesce(parameters('txt'), createArray())[copyIndex()].name]" }, "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } + "value": "[tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'metadata')]" }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "txtRecords": { - "type": "array", - "items": { - "type": "object", - "properties": { - "value": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. The text value of this TXT record." - } - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. The list of TXT records in the record set." - } - } - } - }, - "nullable": true - }, - "virtualNetworkLinkType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "minLength": 1, - "maxLength": 80, - "metadata": { - "description": "Optional. The resource name." - } - }, - "virtualNetworkResourceId": { - "type": "string", - "metadata": { - "description": "Required. The resource ID of the virtual network to link." - } - }, - "location": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The Azure Region where the resource lives." - } - }, - "registrationEnabled": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Is auto-registration of virtual machine records in the virtual network in the Private DNS zone enabled?." - } - }, - "tags": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Resource tags." - } - }, - "resolutionPolicy": { - "type": "string", - "allowedValues": [ - "Default", - "NxDomainRedirect" - ], - "nullable": true, - "metadata": { - "description": "Optional. The resolution type of the private-dns-zone fallback machanism." - } - } - } - }, - "nullable": true - } - }, - "parameters": { - "name": { - "type": "string", - "metadata": { - "description": "Required. Private DNS zone name." - } - }, - "a": { - "$ref": "#/definitions/aType", - "metadata": { - "description": "Optional. Array of A records." - } - }, - "aaaa": { - "$ref": "#/definitions/aaaaType", - "metadata": { - "description": "Optional. Array of AAAA records." - } - }, - "cname": { - "$ref": "#/definitions/cnameType", - "metadata": { - "description": "Optional. Array of CNAME records." - } - }, - "mx": { - "$ref": "#/definitions/mxType", - "metadata": { - "description": "Optional. Array of MX records." - } - }, - "ptr": { - "$ref": "#/definitions/ptrType", - "metadata": { - "description": "Optional. Array of PTR records." - } - }, - "soa": { - "$ref": "#/definitions/soaType", - "metadata": { - "description": "Optional. Array of SOA records." - } - }, - "srv": { - "$ref": "#/definitions/srvType", - "metadata": { - "description": "Optional. Array of SRV records." - } - }, - "txt": { - "$ref": "#/definitions/txtType", - "metadata": { - "description": "Optional. Array of TXT records." - } - }, - "virtualNetworkLinks": { - "$ref": "#/definitions/virtualNetworkLinkType", - "metadata": { - "description": "Optional. Array of custom objects describing vNet links of the DNS zone. Each object should contain properties 'virtualNetworkResourceId' and 'registrationEnabled'. The 'vnetResourceId' is a resource ID of a vNet to link, 'registrationEnabled' (bool) enables automatic DNS registration in the zone for the linked vNet." - } - }, - "location": { - "type": "string", - "defaultValue": "global", - "metadata": { - "description": "Optional. The location of the PrivateDNSZone. Should be global." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "tags": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Tags of the resource." - } - }, - "lock": { - "$ref": "#/definitions/lockType", - "metadata": { - "description": "Optional. The lock settings of the service." - } - }, - "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')]", - "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": "2024-03-01", - "name": "[format('46d3xbcp.res.network-privatednszone.{0}.{1}', replace('0.7.0', '.', '-'), 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" - } - } - } - } - }, - "privateDnsZone": { - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('name')]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]" - }, - "privateDnsZone_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/privateDnsZones/{0}', parameters('name'))]", - "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", - "properties": { - "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", - "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" - }, - "dependsOn": [ - "privateDnsZone" - ] - }, - "privateDnsZone_roleAssignments": { - "copy": { - "name": "privateDnsZone_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}', parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones', 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": [ - "privateDnsZone" - ] - }, - "privateDnsZone_A": { - "copy": { - "name": "privateDnsZone_A", - "count": "[length(coalesce(parameters('a'), createArray()))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-ARecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "privateDnsZoneName": { - "value": "[parameters('name')]" - }, - "name": { - "value": "[coalesce(parameters('a'), createArray())[copyIndex()].name]" - }, - "aRecords": { - "value": "[tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'aRecords')]" - }, - "metadata": { - "value": "[tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'metadata')]" + "txtRecords": { + "value": "[tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'txtRecords')]" }, "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'ttl'), 3600)]" + "value": "[coalesce(tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'ttl'), 3600)]" }, "roleAssignments": { - "value": "[tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'roleAssignments')]" + "value": "[tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'roleAssignments')]" } }, "template": { @@ -31292,10 +26138,10 @@ "_generator": { "name": "bicep", "version": "0.32.4.45862", - "templateHash": "2531120132215940282" + "templateHash": "1855369119498044639" }, - "name": "Private DNS Zone A record", - "description": "This module deploys a Private DNS Zone A record.", + "name": "Private DNS Zone TXT record", + "description": "This module deploys a Private DNS Zone TXT record.", "owner": "Azure/module-maintainers" }, "definitions": { @@ -31383,14 +26229,7 @@ "name": { "type": "string", "metadata": { - "description": "Required. The name of the A record." - } - }, - "aRecords": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. The list of A records in the record set." + "description": "Required. The name of the TXT record." } }, "metadata": { @@ -31407,6 +26246,13 @@ "description": "Optional. The TTL (time-to-live) of the records in the record set." } }, + "txtRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of TXT records in the record set." + } + }, "roleAssignments": { "$ref": "#/definitions/roleAssignmentType", "metadata": { @@ -31439,25 +26285,25 @@ "apiVersion": "2020-06-01", "name": "[parameters('privateDnsZoneName')]" }, - "A": { - "type": "Microsoft.Network/privateDnsZones/A", + "TXT": { + "type": "Microsoft.Network/privateDnsZones/TXT", "apiVersion": "2020-06-01", "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", "properties": { - "aRecords": "[parameters('aRecords')]", "metadata": "[parameters('metadata')]", - "ttl": "[parameters('ttl')]" + "ttl": "[parameters('ttl')]", + "txtRecords": "[parameters('txtRecords')]" } }, - "A_roleAssignments": { + "TXT_roleAssignments": { "copy": { - "name": "A_roleAssignments", + "name": "TXT_roleAssignments", "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/A/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/A', parameters('privateDnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/TXT/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/TXT', parameters('privateDnsZoneName'), 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]", @@ -31468,7 +26314,7 @@ "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" }, "dependsOn": [ - "A" + "TXT" ] } }, @@ -31476,21 +26322,21 @@ "name": { "type": "string", "metadata": { - "description": "The name of the deployed A record." + "description": "The name of the deployed TXT record." }, "value": "[parameters('name')]" }, "resourceId": { "type": "string", "metadata": { - "description": "The resource ID of the deployed A record." + "description": "The resource ID of the deployed TXT record." }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/A', parameters('privateDnsZoneName'), parameters('name'))]" + "value": "[resourceId('Microsoft.Network/privateDnsZones/TXT', parameters('privateDnsZoneName'), parameters('name'))]" }, "resourceGroupName": { "type": "string", "metadata": { - "description": "The resource group of the deployed A record." + "description": "The resource group of the deployed TXT record." }, "value": "[resourceGroup().name]" } @@ -31501,14 +26347,14 @@ "privateDnsZone" ] }, - "privateDnsZone_AAAA": { + "privateDnsZone_virtualNetworkLinks": { "copy": { - "name": "privateDnsZone_AAAA", - "count": "[length(coalesce(parameters('aaaa'), createArray()))]" + "name": "privateDnsZone_virtualNetworkLinks", + "count": "[length(coalesce(parameters('virtualNetworkLinks'), createArray()))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-AAAARecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "name": "[format('{0}-PrivateDnsZone-VirtualNetworkLink-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -31519,19 +26365,22 @@ "value": "[parameters('name')]" }, "name": { - "value": "[coalesce(parameters('aaaa'), createArray())[copyIndex()].name]" + "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'name'), format('{0}-vnetlink', last(split(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()].virtualNetworkResourceId, '/'))))]" }, - "aaaaRecords": { - "value": "[tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'aaaaRecords')]" + "virtualNetworkResourceId": { + "value": "[coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()].virtualNetworkResourceId]" }, - "metadata": { - "value": "[tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'metadata')]" + "location": { + "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'location'), 'global')]" }, - "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'ttl'), 3600)]" + "registrationEnabled": { + "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'registrationEnabled'), false())]" }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'roleAssignments')]" + "tags": { + "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + }, + "resolutionPolicy": { + "value": "[tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'resolutionPolicy')]" } }, "template": { @@ -31542,87 +26391,12 @@ "_generator": { "name": "bicep", "version": "0.32.4.45862", - "templateHash": "16709340450244912125" + "templateHash": "15326596012552051215" }, - "name": "Private DNS Zone AAAA record", - "description": "This module deploys a Private DNS Zone AAAA record.", + "name": "Private DNS Zone Virtual Network Link", + "description": "This module deploys a Private DNS Zone Virtual Network Link.", "owner": "Azure/module-maintainers" }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } - } - } - }, - "nullable": true - } - }, "parameters": { "privateDnsZoneName": { "type": "string", @@ -31632,54 +26406,44 @@ }, "name": { "type": "string", + "defaultValue": "[format('{0}-vnetlink', last(split(parameters('virtualNetworkResourceId'), '/')))]", "metadata": { - "description": "Required. The name of the AAAA record." + "description": "Optional. The name of the virtual network link." } }, - "aaaaRecords": { - "type": "array", - "nullable": true, + "location": { + "type": "string", + "defaultValue": "global", "metadata": { - "description": "Optional. The list of AAAA records in the record set." + "description": "Optional. The location of the PrivateDNSZone. Should be global." } }, - "metadata": { + "tags": { "type": "object", "nullable": true, "metadata": { - "description": "Optional. The metadata attached to the record set." + "description": "Optional. Tags of the resource." } }, - "ttl": { - "type": "int", - "defaultValue": 3600, + "registrationEnabled": { + "type": "bool", + "defaultValue": false, "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." + "description": "Optional. Is auto-registration of virtual machine records in the virtual network in the Private DNS zone enabled?." } }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", + "virtualNetworkResourceId": { + "type": "string", "metadata": { - "description": "Optional. Array of role assignments to create." + "description": "Required. Link to another virtual network resource ID." } - } - }, - "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)))))]" + }, + "resolutionPolicy": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resolution policy on the virtual network link. Only applicable for virtual network links to privatelink zones, and for A,AAAA,CNAME queries. When set to `NxDomainRedirect`, Azure DNS resolver falls back to public resolution if private dns query resolution results in non-existent domain response. `Default` is configured as the default option." } - ], - "builtInRoleNames": { - "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", - "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, "resources": { @@ -31689,60 +26453,49 @@ "apiVersion": "2020-06-01", "name": "[parameters('privateDnsZoneName')]" }, - "AAAA": { - "type": "Microsoft.Network/privateDnsZones/AAAA", - "apiVersion": "2020-06-01", + "virtualNetworkLink": { + "type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks", + "apiVersion": "2024-06-01", "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", "properties": { - "aaaaRecords": "[parameters('aaaaRecords')]", - "metadata": "[parameters('metadata')]", - "ttl": "[parameters('ttl')]" + "registrationEnabled": "[parameters('registrationEnabled')]", + "virtualNetwork": { + "id": "[parameters('virtualNetworkResourceId')]" + }, + "resolutionPolicy": "[parameters('resolutionPolicy')]" } - }, - "AAAA_roleAssignments": { - "copy": { - "name": "AAAA_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/AAAA/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/AAAA', parameters('privateDnsZoneName'), 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": [ - "AAAA" - ] } }, "outputs": { "name": { "type": "string", "metadata": { - "description": "The name of the deployed AAAA record." + "description": "The name of the deployed virtual network link." }, "value": "[parameters('name')]" }, "resourceId": { "type": "string", "metadata": { - "description": "The resource ID of the deployed AAAA record." + "description": "The resource ID of the deployed virtual network link." }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/AAAA', parameters('privateDnsZoneName'), parameters('name'))]" + "value": "[resourceId('Microsoft.Network/privateDnsZones/virtualNetworkLinks', parameters('privateDnsZoneName'), parameters('name'))]" }, "resourceGroupName": { "type": "string", "metadata": { - "description": "The resource group of the deployed AAAA record." + "description": "The resource group of the deployed virtual network link." }, "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('virtualNetworkLink', '2024-06-01', 'full').location]" } } } @@ -31750,765 +26503,901 @@ "dependsOn": [ "privateDnsZone" ] + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the private DNS zone was deployed into." + }, + "value": "[resourceGroup().name]" }, - "privateDnsZone_CNAME": { - "copy": { - "name": "privateDnsZone_CNAME", - "count": "[length(coalesce(parameters('cname'), createArray()))]" + "name": { + "type": "string", + "metadata": { + "description": "The name of the private DNS zone." }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-CNAMERecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "privateDnsZoneName": { - "value": "[parameters('name')]" - }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the private DNS zone." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones', parameters('name'))]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('privateDnsZone', '2020-06-01', 'full').location]" + } + } + } + } + }, + "filePrivateDnsZone": { + "condition": "[parameters('networkIsolation')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "private-dns-file-deployment", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[format('privatelink.file.{0}', environment().suffixes.storage)]" + }, + "virtualNetworkLinks": { + "value": [ + { + "virtualNetworkResourceId": "[parameters('virtualNetworkResourceId')]" + } + ] + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "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.32.4.45862", + "templateHash": "83178825086050429" + }, + "name": "Private DNS Zones", + "description": "This module deploys a Private DNS zone.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { "name": { - "value": "[coalesce(parameters('cname'), createArray())[copyIndex()].name]" + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } }, - "cnameRecord": { - "value": "[tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'cnameRecord')]" + "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'." + } }, - "metadata": { - "value": "[tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'metadata')]" + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } }, - "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'ttl'), 3600)]" + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'roleAssignments')]" + "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." + } + } + } + }, + "nullable": true + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." } }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "languageVersion": "2.0", - "contentVersion": "1.0.0.0", + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, "metadata": { - "_generator": { - "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "9976020649752073181" - }, - "name": "Private DNS Zone CNAME record", - "description": "This module deploys a Private DNS Zone CNAME record.", - "owner": "Azure/module-maintainers" + "description": "Optional. Specify the type of lock." + } + } + }, + "nullable": true + }, + "aType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } - } - } - }, - "nullable": true + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." } }, - "parameters": { - "privateDnsZoneName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the CNAME record." - } - }, - "cnameRecord": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. A CNAME record." - } - }, + "ttl": { + "type": "int", + "nullable": true, "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata attached to the record set." - } - }, - "ttl": { - "type": "int", - "defaultValue": 3600, - "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } + "description": "Optional. The TTL of the record." } }, - "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')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", - "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." } }, - "resources": { - "privateDnsZone": { - "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" - }, - "CNAME": { - "type": "Microsoft.Network/privateDnsZones/CNAME", - "apiVersion": "2020-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "aRecords": { + "type": "array", + "items": { + "type": "object", "properties": { - "cnameRecord": "[parameters('cnameRecord')]", - "metadata": "[parameters('metadata')]", - "ttl": "[parameters('ttl')]" + "ipv4Address": { + "type": "string", + "metadata": { + "description": "Required. The IPv4 address of this A record." + } + } } }, - "CNAME_roleAssignments": { - "copy": { - "name": "CNAME_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/CNAME/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/CNAME', parameters('privateDnsZoneName'), 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": [ - "CNAME" - ] - } - }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the deployed CNAME record." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the deployed CNAME record." - }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/CNAME', parameters('privateDnsZoneName'), parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group of the deployed CNAME record." - }, - "value": "[resourceGroup().name]" + "nullable": true, + "metadata": { + "description": "Optional. The list of A records in the record set." } } } }, - "dependsOn": [ - "privateDnsZone" - ] + "nullable": true }, - "privateDnsZone_MX": { - "copy": { - "name": "privateDnsZone_MX", - "count": "[length(coalesce(parameters('mx'), createArray()))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-MXRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "privateDnsZoneName": { - "value": "[parameters('name')]" - }, + "aaaaType": { + "type": "array", + "items": { + "type": "object", + "properties": { "name": { - "value": "[coalesce(parameters('mx'), createArray())[copyIndex()].name]" + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } }, "metadata": { - "value": "[tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'metadata')]" - }, - "mxRecords": { - "value": "[tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'mxRecords')]" + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } }, "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'ttl'), 3600)]" + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } }, "roleAssignments": { - "value": "[tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'roleAssignments')]" - } - }, - "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.32.4.45862", - "templateHash": "2520323624213076361" - }, - "name": "Private DNS Zone MX record", - "description": "This module deploys a Private DNS Zone MX record.", - "owner": "Azure/module-maintainers" + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } + "aaaaRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "ipv6Address": { + "type": "string", + "metadata": { + "description": "Required. The IPv6 address of this AAAA record." } } - }, - "nullable": true - } - }, - "parameters": { - "privateDnsZoneName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the MX record." } }, + "nullable": true, "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata attached to the record set." - } - }, - "mxRecords": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. The list of MX records in the record set." - } - }, - "ttl": { - "type": "int", - "defaultValue": 3600, - "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } + "description": "Optional. The list of AAAA records in the record set." + } + } + } + }, + "nullable": true + }, + "cnameType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." } }, - "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')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", - "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." } }, - "resources": { - "privateDnsZone": { - "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" - }, - "MX": { - "type": "Microsoft.Network/privateDnsZones/MX", - "apiVersion": "2020-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "properties": { - "metadata": "[parameters('metadata')]", - "mxRecords": "[parameters('mxRecords')]", - "ttl": "[parameters('ttl')]" - } - }, - "MX_roleAssignments": { - "copy": { - "name": "MX_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/MX/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/MX', parameters('privateDnsZoneName'), 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": [ - "MX" - ] + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." } }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the deployed MX record." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the deployed MX record." - }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/MX', parameters('privateDnsZoneName'), parameters('name'))]" + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "cnameRecord": { + "type": "object", + "properties": { + "cname": { + "type": "string", + "metadata": { + "description": "Required. The canonical name of the CNAME record." + } + } }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group of the deployed MX record." - }, - "value": "[resourceGroup().name]" + "nullable": true, + "metadata": { + "description": "Optional. The CNAME record in the record set." } } } }, - "dependsOn": [ - "privateDnsZone" - ] + "nullable": true }, - "privateDnsZone_PTR": { - "copy": { - "name": "privateDnsZone_PTR", - "count": "[length(coalesce(parameters('ptr'), createArray()))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-PTRRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "privateDnsZoneName": { - "value": "[parameters('name')]" - }, + "mxType": { + "type": "array", + "items": { + "type": "object", + "properties": { "name": { - "value": "[coalesce(parameters('ptr'), createArray())[copyIndex()].name]" + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } }, "metadata": { - "value": "[tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'metadata')]" - }, - "ptrRecords": { - "value": "[tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'ptrRecords')]" + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } }, "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'ttl'), 3600)]" + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } }, "roleAssignments": { - "value": "[tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'roleAssignments')]" - } - }, - "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.32.4.45862", - "templateHash": "3080404733048745471" - }, - "name": "Private DNS Zone PTR record", - "description": "This module deploys a Private DNS Zone PTR record.", - "owner": "Azure/module-maintainers" + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } + "mxRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "exchange": { + "type": "string", + "metadata": { + "description": "Required. The domain name of the mail host for this MX record." + } + }, + "preference": { + "type": "int", + "metadata": { + "description": "Required. The preference value for this MX record." } } - }, - "nullable": true - } - }, - "parameters": { - "privateDnsZoneName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the PTR record." } }, + "nullable": true, + "metadata": { + "description": "Optional. The list of MX records in the record set." + } + } + } + }, + "nullable": true + }, + "ptrType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "ptrRecords": { + "type": "array", + "items": { "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata attached to the record set." - } - }, - "ptrRecords": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. The list of PTR records in the record set." - } - }, - "ttl": { - "type": "int", - "defaultValue": 3600, - "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." + "properties": { + "ptrdname": { + "type": "string", + "metadata": { + "description": "Required. The PTR target domain name for this PTR record." + } + } } }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } + "nullable": true, + "metadata": { + "description": "Optional. The list of PTR records in the record set." + } + } + } + }, + "nullable": true + }, + "soaType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." } }, - "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')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", - "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." } }, - "resources": { - "privateDnsZone": { - "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "soaRecord": { + "type": "object", + "properties": { + "email": { + "type": "string", + "metadata": { + "description": "Required. The email contact for this SOA record." + } + }, + "expireTime": { + "type": "int", + "metadata": { + "description": "Required. The expire time for this SOA record." + } + }, + "host": { + "type": "string", + "metadata": { + "description": "Required. The domain name of the authoritative name server for this SOA record." + } + }, + "minimumTtl": { + "type": "int", + "metadata": { + "description": "Required. The minimum value for this SOA record. By convention this is used to determine the negative caching duration." + } + }, + "refreshTime": { + "type": "int", + "metadata": { + "description": "Required. The refresh value for this SOA record." + } + }, + "retryTime": { + "type": "int", + "metadata": { + "description": "Required. The retry time for this SOA record." + } + }, + "serialNumber": { + "type": "int", + "metadata": { + "description": "Required. The serial number for this SOA record." + } + } }, - "PTR": { - "type": "Microsoft.Network/privateDnsZones/PTR", - "apiVersion": "2020-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "nullable": true, + "metadata": { + "description": "Optional. The SOA record in the record set." + } + } + } + }, + "nullable": true + }, + "srvType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "srvRecords": { + "type": "array", + "items": { + "type": "object", "properties": { - "metadata": "[parameters('metadata')]", - "ptrRecords": "[parameters('ptrRecords')]", - "ttl": "[parameters('ttl')]" + "priority": { + "type": "int", + "metadata": { + "description": "Required. The priority value for this SRV record." + } + }, + "weight": { + "type": "int", + "metadata": { + "description": "Required. The weight value for this SRV record." + } + }, + "port": { + "type": "int", + "metadata": { + "description": "Required. The port value for this SRV record." + } + }, + "target": { + "type": "string", + "metadata": { + "description": "Required. The target domain name for this SRV record." + } + } } }, - "PTR_roleAssignments": { - "copy": { - "name": "PTR_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/PTR/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/PTR', parameters('privateDnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "nullable": true, + "metadata": { + "description": "Optional. The list of SRV records in the record set." + } + } + } + }, + "nullable": true + }, + "txtType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "txtRecords": { + "type": "array", + "items": { + "type": "object", "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": [ - "PTR" - ] + "value": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The text value of this TXT record." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of TXT records in the record set." + } + } + } + }, + "nullable": true + }, + "virtualNetworkLinkType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "minLength": 1, + "maxLength": 80, + "metadata": { + "description": "Optional. The resource name." + } + }, + "virtualNetworkResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the virtual network to link." + } + }, + "location": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Azure Region where the resource lives." + } + }, + "registrationEnabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Is auto-registration of virtual machine records in the virtual network in the Private DNS zone enabled?." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Resource tags." } }, + "resolutionPolicy": { + "type": "string", + "allowedValues": [ + "Default", + "NxDomainRedirect" + ], + "nullable": true, + "metadata": { + "description": "Optional. The resolution type of the private-dns-zone fallback machanism." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Private DNS zone name." + } + }, + "a": { + "$ref": "#/definitions/aType", + "metadata": { + "description": "Optional. Array of A records." + } + }, + "aaaa": { + "$ref": "#/definitions/aaaaType", + "metadata": { + "description": "Optional. Array of AAAA records." + } + }, + "cname": { + "$ref": "#/definitions/cnameType", + "metadata": { + "description": "Optional. Array of CNAME records." + } + }, + "mx": { + "$ref": "#/definitions/mxType", + "metadata": { + "description": "Optional. Array of MX records." + } + }, + "ptr": { + "$ref": "#/definitions/ptrType", + "metadata": { + "description": "Optional. Array of PTR records." + } + }, + "soa": { + "$ref": "#/definitions/soaType", + "metadata": { + "description": "Optional. Array of SOA records." + } + }, + "srv": { + "$ref": "#/definitions/srvType", + "metadata": { + "description": "Optional. Array of SRV records." + } + }, + "txt": { + "$ref": "#/definitions/txtType", + "metadata": { + "description": "Optional. Array of TXT records." + } + }, + "virtualNetworkLinks": { + "$ref": "#/definitions/virtualNetworkLinkType", + "metadata": { + "description": "Optional. Array of custom objects describing vNet links of the DNS zone. Each object should contain properties 'virtualNetworkResourceId' and 'registrationEnabled'. The 'vnetResourceId' is a resource ID of a vNet to link, 'registrationEnabled' (bool) enables automatic DNS registration in the zone for the linked vNet." + } + }, + "location": { + "type": "string", + "defaultValue": "global", + "metadata": { + "description": "Optional. The location of the PrivateDNSZone. Should be global." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "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')]", + "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": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privatednszone.{0}.{1}', replace('0.7.0', '.', '-'), 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": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the deployed PTR record." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the deployed PTR record." - }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/PTR', parameters('privateDnsZoneName'), parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group of the deployed PTR record." - }, - "value": "[resourceGroup().name]" + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" } } } + } + }, + "privateDnsZone": { + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]" + }, + "privateDnsZone_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/privateDnsZones/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" }, "dependsOn": [ "privateDnsZone" ] }, - "privateDnsZone_SOA": { + "privateDnsZone_roleAssignments": { "copy": { - "name": "privateDnsZone_SOA", - "count": "[length(coalesce(parameters('soa'), createArray()))]" + "name": "privateDnsZone_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones', 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": [ + "privateDnsZone" + ] + }, + "privateDnsZone_A": { + "copy": { + "name": "privateDnsZone_A", + "count": "[length(coalesce(parameters('a'), createArray()))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-SOARecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "name": "[format('{0}-PrivateDnsZone-ARecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -32519,19 +27408,19 @@ "value": "[parameters('name')]" }, "name": { - "value": "[coalesce(parameters('soa'), createArray())[copyIndex()].name]" + "value": "[coalesce(parameters('a'), createArray())[copyIndex()].name]" }, - "metadata": { - "value": "[tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'metadata')]" + "aRecords": { + "value": "[tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'aRecords')]" }, - "soaRecord": { - "value": "[tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'soaRecord')]" + "metadata": { + "value": "[tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'metadata')]" }, "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'ttl'), 3600)]" + "value": "[coalesce(tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'ttl'), 3600)]" }, "roleAssignments": { - "value": "[tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'roleAssignments')]" + "value": "[tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'roleAssignments')]" } }, "template": { @@ -32542,11 +27431,11 @@ "_generator": { "name": "bicep", "version": "0.32.4.45862", - "templateHash": "6653951445614700931" + "templateHash": "2531120132215940282" }, - "name": "Private DNS Zone SOA record", - "description": "This module deploys a Private DNS Zone SOA record.", - "owner": "Azure/module-maintainers" + "name": "Private DNS Zone A record", + "description": "This module deploys a Private DNS Zone A record.", + "owner": "Azure/module-maintainers" }, "definitions": { "roleAssignmentType": { @@ -32633,21 +27522,21 @@ "name": { "type": "string", "metadata": { - "description": "Required. The name of the SOA record." + "description": "Required. The name of the A record." } }, - "metadata": { - "type": "object", + "aRecords": { + "type": "array", "nullable": true, "metadata": { - "description": "Optional. The metadata attached to the record set." + "description": "Optional. The list of A records in the record set." } }, - "soaRecord": { + "metadata": { "type": "object", "nullable": true, "metadata": { - "description": "Optional. A SOA record." + "description": "Optional. The metadata attached to the record set." } }, "ttl": { @@ -32689,25 +27578,25 @@ "apiVersion": "2020-06-01", "name": "[parameters('privateDnsZoneName')]" }, - "SOA": { - "type": "Microsoft.Network/privateDnsZones/SOA", + "A": { + "type": "Microsoft.Network/privateDnsZones/A", "apiVersion": "2020-06-01", "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", "properties": { + "aRecords": "[parameters('aRecords')]", "metadata": "[parameters('metadata')]", - "soaRecord": "[parameters('soaRecord')]", "ttl": "[parameters('ttl')]" } }, - "SOA_roleAssignments": { + "A_roleAssignments": { "copy": { - "name": "SOA_roleAssignments", + "name": "A_roleAssignments", "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/SOA/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/SOA', parameters('privateDnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/A/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/A', parameters('privateDnsZoneName'), 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]", @@ -32718,7 +27607,7 @@ "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" }, "dependsOn": [ - "SOA" + "A" ] } }, @@ -32726,21 +27615,21 @@ "name": { "type": "string", "metadata": { - "description": "The name of the deployed SOA record." + "description": "The name of the deployed A record." }, "value": "[parameters('name')]" }, "resourceId": { "type": "string", "metadata": { - "description": "The resource ID of the deployed SOA record." + "description": "The resource ID of the deployed A record." }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/SOA', parameters('privateDnsZoneName'), parameters('name'))]" + "value": "[resourceId('Microsoft.Network/privateDnsZones/A', parameters('privateDnsZoneName'), parameters('name'))]" }, "resourceGroupName": { "type": "string", "metadata": { - "description": "The resource group of the deployed SOA record." + "description": "The resource group of the deployed A record." }, "value": "[resourceGroup().name]" } @@ -32751,14 +27640,14 @@ "privateDnsZone" ] }, - "privateDnsZone_SRV": { + "privateDnsZone_AAAA": { "copy": { - "name": "privateDnsZone_SRV", - "count": "[length(coalesce(parameters('srv'), createArray()))]" + "name": "privateDnsZone_AAAA", + "count": "[length(coalesce(parameters('aaaa'), createArray()))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-SRVRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "name": "[format('{0}-PrivateDnsZone-AAAARecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -32769,19 +27658,19 @@ "value": "[parameters('name')]" }, "name": { - "value": "[coalesce(parameters('srv'), createArray())[copyIndex()].name]" + "value": "[coalesce(parameters('aaaa'), createArray())[copyIndex()].name]" }, - "metadata": { - "value": "[tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'metadata')]" + "aaaaRecords": { + "value": "[tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'aaaaRecords')]" }, - "srvRecords": { - "value": "[tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'srvRecords')]" + "metadata": { + "value": "[tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'metadata')]" }, "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'ttl'), 3600)]" + "value": "[coalesce(tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'ttl'), 3600)]" }, "roleAssignments": { - "value": "[tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'roleAssignments')]" + "value": "[tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'roleAssignments')]" } }, "template": { @@ -32792,10 +27681,10 @@ "_generator": { "name": "bicep", "version": "0.32.4.45862", - "templateHash": "5790774778713328446" + "templateHash": "16709340450244912125" }, - "name": "Private DNS Zone SRV record", - "description": "This module deploys a Private DNS Zone SRV record.", + "name": "Private DNS Zone AAAA record", + "description": "This module deploys a Private DNS Zone AAAA record.", "owner": "Azure/module-maintainers" }, "definitions": { @@ -32883,21 +27772,21 @@ "name": { "type": "string", "metadata": { - "description": "Required. The name of the SRV record." + "description": "Required. The name of the AAAA record." } }, - "metadata": { - "type": "object", + "aaaaRecords": { + "type": "array", "nullable": true, "metadata": { - "description": "Optional. The metadata attached to the record set." + "description": "Optional. The list of AAAA records in the record set." } }, - "srvRecords": { - "type": "array", + "metadata": { + "type": "object", "nullable": true, "metadata": { - "description": "Optional. The list of SRV records in the record set." + "description": "Optional. The metadata attached to the record set." } }, "ttl": { @@ -32939,25 +27828,25 @@ "apiVersion": "2020-06-01", "name": "[parameters('privateDnsZoneName')]" }, - "SRV": { - "type": "Microsoft.Network/privateDnsZones/SRV", + "AAAA": { + "type": "Microsoft.Network/privateDnsZones/AAAA", "apiVersion": "2020-06-01", "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", "properties": { + "aaaaRecords": "[parameters('aaaaRecords')]", "metadata": "[parameters('metadata')]", - "srvRecords": "[parameters('srvRecords')]", "ttl": "[parameters('ttl')]" } }, - "SRV_roleAssignments": { + "AAAA_roleAssignments": { "copy": { - "name": "SRV_roleAssignments", + "name": "AAAA_roleAssignments", "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/SRV/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/SRV', parameters('privateDnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/AAAA/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/AAAA', parameters('privateDnsZoneName'), 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]", @@ -32968,7 +27857,7 @@ "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" }, "dependsOn": [ - "SRV" + "AAAA" ] } }, @@ -32976,21 +27865,21 @@ "name": { "type": "string", "metadata": { - "description": "The name of the deployed SRV record." + "description": "The name of the deployed AAAA record." }, "value": "[parameters('name')]" }, "resourceId": { "type": "string", "metadata": { - "description": "The resource ID of the deployed SRV record." + "description": "The resource ID of the deployed AAAA record." }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/SRV', parameters('privateDnsZoneName'), parameters('name'))]" + "value": "[resourceId('Microsoft.Network/privateDnsZones/AAAA', parameters('privateDnsZoneName'), parameters('name'))]" }, "resourceGroupName": { "type": "string", "metadata": { - "description": "The resource group of the deployed SRV record." + "description": "The resource group of the deployed AAAA record." }, "value": "[resourceGroup().name]" } @@ -33001,14 +27890,14 @@ "privateDnsZone" ] }, - "privateDnsZone_TXT": { + "privateDnsZone_CNAME": { "copy": { - "name": "privateDnsZone_TXT", - "count": "[length(coalesce(parameters('txt'), createArray()))]" + "name": "privateDnsZone_CNAME", + "count": "[length(coalesce(parameters('cname'), createArray()))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-TXTRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "name": "[format('{0}-PrivateDnsZone-CNAMERecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -33019,19 +27908,19 @@ "value": "[parameters('name')]" }, "name": { - "value": "[coalesce(parameters('txt'), createArray())[copyIndex()].name]" + "value": "[coalesce(parameters('cname'), createArray())[copyIndex()].name]" }, - "metadata": { - "value": "[tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'metadata')]" + "cnameRecord": { + "value": "[tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'cnameRecord')]" }, - "txtRecords": { - "value": "[tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'txtRecords')]" + "metadata": { + "value": "[tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'metadata')]" }, "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'ttl'), 3600)]" + "value": "[coalesce(tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'ttl'), 3600)]" }, "roleAssignments": { - "value": "[tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'roleAssignments')]" + "value": "[tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'roleAssignments')]" } }, "template": { @@ -33042,10 +27931,10 @@ "_generator": { "name": "bicep", "version": "0.32.4.45862", - "templateHash": "1855369119498044639" + "templateHash": "9976020649752073181" }, - "name": "Private DNS Zone TXT record", - "description": "This module deploys a Private DNS Zone TXT record.", + "name": "Private DNS Zone CNAME record", + "description": "This module deploys a Private DNS Zone CNAME record.", "owner": "Azure/module-maintainers" }, "definitions": { @@ -33133,7 +28022,14 @@ "name": { "type": "string", "metadata": { - "description": "Required. The name of the TXT record." + "description": "Required. The name of the CNAME record." + } + }, + "cnameRecord": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. A CNAME record." } }, "metadata": { @@ -33150,13 +28046,6 @@ "description": "Optional. The TTL (time-to-live) of the records in the record set." } }, - "txtRecords": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. The list of TXT records in the record set." - } - }, "roleAssignments": { "$ref": "#/definitions/roleAssignmentType", "metadata": { @@ -33189,25 +28078,25 @@ "apiVersion": "2020-06-01", "name": "[parameters('privateDnsZoneName')]" }, - "TXT": { - "type": "Microsoft.Network/privateDnsZones/TXT", + "CNAME": { + "type": "Microsoft.Network/privateDnsZones/CNAME", "apiVersion": "2020-06-01", "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", "properties": { + "cnameRecord": "[parameters('cnameRecord')]", "metadata": "[parameters('metadata')]", - "ttl": "[parameters('ttl')]", - "txtRecords": "[parameters('txtRecords')]" + "ttl": "[parameters('ttl')]" } }, - "TXT_roleAssignments": { + "CNAME_roleAssignments": { "copy": { - "name": "TXT_roleAssignments", + "name": "CNAME_roleAssignments", "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/TXT/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/TXT', parameters('privateDnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/CNAME/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/CNAME', parameters('privateDnsZoneName'), 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]", @@ -33218,7 +28107,7 @@ "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" }, "dependsOn": [ - "TXT" + "CNAME" ] } }, @@ -33226,21 +28115,21 @@ "name": { "type": "string", "metadata": { - "description": "The name of the deployed TXT record." + "description": "The name of the deployed CNAME record." }, "value": "[parameters('name')]" }, "resourceId": { "type": "string", "metadata": { - "description": "The resource ID of the deployed TXT record." + "description": "The resource ID of the deployed CNAME record." }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/TXT', parameters('privateDnsZoneName'), parameters('name'))]" + "value": "[resourceId('Microsoft.Network/privateDnsZones/CNAME', parameters('privateDnsZoneName'), parameters('name'))]" }, "resourceGroupName": { "type": "string", "metadata": { - "description": "The resource group of the deployed TXT record." + "description": "The resource group of the deployed CNAME record." }, "value": "[resourceGroup().name]" } @@ -33251,14 +28140,14 @@ "privateDnsZone" ] }, - "privateDnsZone_virtualNetworkLinks": { + "privateDnsZone_MX": { "copy": { - "name": "privateDnsZone_virtualNetworkLinks", - "count": "[length(coalesce(parameters('virtualNetworkLinks'), createArray()))]" + "name": "privateDnsZone_MX", + "count": "[length(coalesce(parameters('mx'), createArray()))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-VirtualNetworkLink-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "name": "[format('{0}-PrivateDnsZone-MXRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -33269,22 +28158,19 @@ "value": "[parameters('name')]" }, "name": { - "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'name'), format('{0}-vnetlink', last(split(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()].virtualNetworkResourceId, '/'))))]" - }, - "virtualNetworkResourceId": { - "value": "[coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()].virtualNetworkResourceId]" + "value": "[coalesce(parameters('mx'), createArray())[copyIndex()].name]" }, - "location": { - "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'location'), 'global')]" + "metadata": { + "value": "[tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'metadata')]" }, - "registrationEnabled": { - "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'registrationEnabled'), false())]" + "mxRecords": { + "value": "[tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'mxRecords')]" }, - "tags": { - "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'ttl'), 3600)]" }, - "resolutionPolicy": { - "value": "[tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'resolutionPolicy')]" + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'roleAssignments')]" } }, "template": { @@ -33295,12 +28181,87 @@ "_generator": { "name": "bicep", "version": "0.32.4.45862", - "templateHash": "15326596012552051215" + "templateHash": "2520323624213076361" }, - "name": "Private DNS Zone Virtual Network Link", - "description": "This module deploys a Private DNS Zone Virtual Network Link.", + "name": "Private DNS Zone MX record", + "description": "This module deploys a Private DNS Zone MX record.", "owner": "Azure/module-maintainers" }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "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." + } + } + } + }, + "nullable": true + } + }, "parameters": { "privateDnsZoneName": { "type": "string", @@ -33310,44 +28271,54 @@ }, "name": { "type": "string", - "defaultValue": "[format('{0}-vnetlink', last(split(parameters('virtualNetworkResourceId'), '/')))]", - "metadata": { - "description": "Optional. The name of the virtual network link." - } - }, - "location": { - "type": "string", - "defaultValue": "global", "metadata": { - "description": "Optional. The location of the PrivateDNSZone. Should be global." + "description": "Required. The name of the MX record." } }, - "tags": { + "metadata": { "type": "object", "nullable": true, "metadata": { - "description": "Optional. Tags of the resource." + "description": "Optional. The metadata attached to the record set." } }, - "registrationEnabled": { - "type": "bool", - "defaultValue": false, + "mxRecords": { + "type": "array", + "nullable": true, "metadata": { - "description": "Optional. Is auto-registration of virtual machine records in the virtual network in the Private DNS zone enabled?." + "description": "Optional. The list of MX records in the record set." } }, - "virtualNetworkResourceId": { - "type": "string", + "ttl": { + "type": "int", + "defaultValue": 3600, "metadata": { - "description": "Required. Link to another virtual network resource ID." + "description": "Optional. The TTL (time-to-live) of the records in the record set." } }, - "resolutionPolicy": { - "type": "string", - "nullable": true, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", "metadata": { - "description": "Optional. The resolution policy on the virtual network link. Only applicable for virtual network links to privatelink zones, and for A,AAAA,CNAME queries. When set to `NxDomainRedirect`, Azure DNS resolver falls back to public resolution if private dns query resolution results in non-existent domain response. `Default` is configured as the default option." + "description": "Optional. Array of role assignments to create." + } + } + }, + "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')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, "resources": { @@ -33357,49 +28328,60 @@ "apiVersion": "2020-06-01", "name": "[parameters('privateDnsZoneName')]" }, - "virtualNetworkLink": { - "type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks", - "apiVersion": "2024-06-01", + "MX": { + "type": "Microsoft.Network/privateDnsZones/MX", + "apiVersion": "2020-06-01", "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", "properties": { - "registrationEnabled": "[parameters('registrationEnabled')]", - "virtualNetwork": { - "id": "[parameters('virtualNetworkResourceId')]" - }, - "resolutionPolicy": "[parameters('resolutionPolicy')]" + "metadata": "[parameters('metadata')]", + "mxRecords": "[parameters('mxRecords')]", + "ttl": "[parameters('ttl')]" } + }, + "MX_roleAssignments": { + "copy": { + "name": "MX_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/MX/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/MX', parameters('privateDnsZoneName'), 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": [ + "MX" + ] } }, "outputs": { "name": { "type": "string", "metadata": { - "description": "The name of the deployed virtual network link." + "description": "The name of the deployed MX record." }, "value": "[parameters('name')]" }, "resourceId": { "type": "string", "metadata": { - "description": "The resource ID of the deployed virtual network link." + "description": "The resource ID of the deployed MX record." }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/virtualNetworkLinks', parameters('privateDnsZoneName'), parameters('name'))]" + "value": "[resourceId('Microsoft.Network/privateDnsZones/MX', parameters('privateDnsZoneName'), parameters('name'))]" }, "resourceGroupName": { "type": "string", "metadata": { - "description": "The resource group of the deployed virtual network link." + "description": "The resource group of the deployed MX record." }, "value": "[resourceGroup().name]" - }, - "location": { - "type": "string", - "metadata": { - "description": "The location the resource was deployed into." - }, - "value": "[reference('virtualNetworkLink', '2024-06-01', 'full').location]" } } } @@ -33407,901 +28389,765 @@ "dependsOn": [ "privateDnsZone" ] - } - }, - "outputs": { - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group the private DNS zone was deployed into." - }, - "value": "[resourceGroup().name]" - }, - "name": { - "type": "string", - "metadata": { - "description": "The name of the private DNS zone." - }, - "value": "[parameters('name')]" }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the private DNS zone." - }, - "value": "[resourceId('Microsoft.Network/privateDnsZones', parameters('name'))]" - }, - "location": { - "type": "string", - "metadata": { - "description": "The location the resource was deployed into." + "privateDnsZone_PTR": { + "copy": { + "name": "privateDnsZone_PTR", + "count": "[length(coalesce(parameters('ptr'), createArray()))]" }, - "value": "[reference('privateDnsZone', '2020-06-01', 'full').location]" - } - } - } - } - }, - "openAiPrivateDnsZone": { - "condition": "[parameters('networkIsolation')]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "private-dns-openai-deployment", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[format('privatelink.openai.{0}', if(equals(toLower(environment().name), 'azureusgovernment'), 'azure.us', 'azure.com'))]" - }, - "virtualNetworkLinks": { - "value": [ - { - "virtualNetworkResourceId": "[parameters('virtualNetworkResourceId')]" - } - ] - }, - "tags": { - "value": "[parameters('tags')]" - } - }, - "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.32.4.45862", - "templateHash": "83178825086050429" - }, - "name": "Private DNS Zones", - "description": "This module deploys a Private DNS zone.", - "owner": "Azure/module-maintainers" - }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "type": "object", - "properties": { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-PTRRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." - } + "value": "[coalesce(parameters('ptr'), createArray())[copyIndex()].name]" }, - "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'." - } + "metadata": { + "value": "[tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'metadata')]" }, - "principalId": { - "type": "string", - "metadata": { - "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." - } + "ptrRecords": { + "value": "[tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'ptrRecords')]" }, - "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." - } + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'ttl'), 3600)]" }, - "delegatedManagedIdentityResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The Resource Id of the delegated managed identity resource." - } - } - } - }, - "nullable": true - }, - "lockType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Specify the name of lock." + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'roleAssignments')]" } }, - "kind": { - "type": "string", - "allowedValues": [ - "CanNotDelete", - "None", - "ReadOnly" - ], - "nullable": true, - "metadata": { - "description": "Optional. Specify the type of lock." - } - } - }, - "nullable": true - }, - "aType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } - }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } + "_generator": { + "name": "bicep", + "version": "0.32.4.45862", + "templateHash": "3080404733048745471" + }, + "name": "Private DNS Zone PTR record", + "description": "This module deploys a Private DNS Zone PTR record.", + "owner": "Azure/module-maintainers" }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "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." + } + } + } + }, + "nullable": true } }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the PTR record." + } + }, "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "aRecords": { - "type": "array", - "items": { "type": "object", - "properties": { - "ipv4Address": { - "type": "string", - "metadata": { - "description": "Required. The IPv4 address of this A record." - } - } + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." } }, - "nullable": true, - "metadata": { - "description": "Optional. The list of A records in the record set." - } - } - } - }, - "nullable": true - }, - "aaaaType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } - }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." + "ptrRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of PTR records in the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } } }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." + "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')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, - "aaaaRecords": { - "type": "array", - "items": { - "type": "object", + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "PTR": { + "type": "Microsoft.Network/privateDnsZones/PTR", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", "properties": { - "ipv6Address": { - "type": "string", - "metadata": { - "description": "Required. The IPv6 address of this AAAA record." - } - } + "metadata": "[parameters('metadata')]", + "ptrRecords": "[parameters('ptrRecords')]", + "ttl": "[parameters('ttl')]" } }, - "nullable": true, - "metadata": { - "description": "Optional. The list of AAAA records in the record set." - } - } - } - }, - "nullable": true - }, - "cnameType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } - }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." + "PTR_roleAssignments": { + "copy": { + "name": "PTR_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/PTR/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/PTR', parameters('privateDnsZoneName'), 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": [ + "PTR" + ] } }, - "cnameRecord": { - "type": "object", - "properties": { - "cname": { - "type": "string", - "metadata": { - "description": "Required. The canonical name of the CNAME record." - } - } + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed PTR record." + }, + "value": "[parameters('name')]" }, - "nullable": true, - "metadata": { - "description": "Optional. The CNAME record in the record set." + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed PTR record." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/PTR', parameters('privateDnsZoneName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed PTR record." + }, + "value": "[resourceGroup().name]" } } } }, - "nullable": true + "dependsOn": [ + "privateDnsZone" + ] }, - "mxType": { - "type": "array", - "items": { - "type": "object", - "properties": { + "privateDnsZone_SOA": { + "copy": { + "name": "privateDnsZone_SOA", + "count": "[length(coalesce(parameters('soa'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-SOARecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } + "value": "[coalesce(parameters('soa'), createArray())[copyIndex()].name]" }, "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } + "value": "[tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'metadata')]" + }, + "soaRecord": { + "value": "[tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'soaRecord')]" }, "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } + "value": "[coalesce(tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'ttl'), 3600)]" }, "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "mxRecords": { - "type": "array", - "items": { - "type": "object", - "properties": { - "exchange": { - "type": "string", - "metadata": { - "description": "Required. The domain name of the mail host for this MX record." - } - }, - "preference": { - "type": "int", - "metadata": { - "description": "Required. The preference value for this MX record." - } - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. The list of MX records in the record set." - } + "value": "[tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'roleAssignments')]" } - } - }, - "nullable": true - }, - "ptrType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } - }, + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } - }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } + "_generator": { + "name": "bicep", + "version": "0.32.4.45862", + "templateHash": "6653951445614700931" + }, + "name": "Private DNS Zone SOA record", + "description": "This module deploys a Private DNS Zone SOA record.", + "owner": "Azure/module-maintainers" }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "ptrRecords": { - "type": "array", - "items": { - "type": "object", - "properties": { - "ptrdname": { - "type": "string", - "metadata": { - "description": "Required. The PTR target domain name for this PTR record." + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "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." + } } } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. The list of PTR records in the record set." - } - } - } - }, - "nullable": true - }, - "soaType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." + }, + "nullable": true } }, - "ttl": { - "type": "int", - "nullable": true, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the SOA record." + } + }, "metadata": { - "description": "Optional. The TTL of the record." + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "soaRecord": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. A SOA record." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } } }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." + "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')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, - "soaRecord": { - "type": "object", - "properties": { - "email": { - "type": "string", - "metadata": { - "description": "Required. The email contact for this SOA record." - } - }, - "expireTime": { - "type": "int", - "metadata": { - "description": "Required. The expire time for this SOA record." - } - }, - "host": { - "type": "string", - "metadata": { - "description": "Required. The domain name of the authoritative name server for this SOA record." - } + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "SOA": { + "type": "Microsoft.Network/privateDnsZones/SOA", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "properties": { + "metadata": "[parameters('metadata')]", + "soaRecord": "[parameters('soaRecord')]", + "ttl": "[parameters('ttl')]" + } + }, + "SOA_roleAssignments": { + "copy": { + "name": "SOA_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, - "minimumTtl": { - "type": "int", - "metadata": { - "description": "Required. The minimum value for this SOA record. By convention this is used to determine the negative caching duration." - } + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/SOA/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/SOA', parameters('privateDnsZoneName'), 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')]" }, - "refreshTime": { - "type": "int", - "metadata": { - "description": "Required. The refresh value for this SOA record." - } + "dependsOn": [ + "SOA" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed SOA record." }, - "retryTime": { - "type": "int", - "metadata": { - "description": "Required. The retry time for this SOA record." - } + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed SOA record." }, - "serialNumber": { - "type": "int", - "metadata": { - "description": "Required. The serial number for this SOA record." - } - } + "value": "[resourceId('Microsoft.Network/privateDnsZones/SOA', parameters('privateDnsZoneName'), parameters('name'))]" }, - "nullable": true, - "metadata": { - "description": "Optional. The SOA record in the record set." + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed SOA record." + }, + "value": "[resourceGroup().name]" } } } }, - "nullable": true + "dependsOn": [ + "privateDnsZone" + ] }, - "srvType": { - "type": "array", - "items": { - "type": "object", - "properties": { + "privateDnsZone_SRV": { + "copy": { + "name": "privateDnsZone_SRV", + "count": "[length(coalesce(parameters('srv'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-SRVRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } + "value": "[coalesce(parameters('srv'), createArray())[copyIndex()].name]" }, "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } + "value": "[tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'metadata')]" + }, + "srvRecords": { + "value": "[tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'srvRecords')]" }, "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } + "value": "[coalesce(tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'ttl'), 3600)]" }, "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } + "value": "[tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'roleAssignments')]" + } + }, + "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.32.4.45862", + "templateHash": "5790774778713328446" + }, + "name": "Private DNS Zone SRV record", + "description": "This module deploys a Private DNS Zone SRV record.", + "owner": "Azure/module-maintainers" }, - "srvRecords": { - "type": "array", - "items": { - "type": "object", - "properties": { - "priority": { - "type": "int", - "metadata": { - "description": "Required. The priority value for this SRV record." - } - }, - "weight": { - "type": "int", - "metadata": { - "description": "Required. The weight value for this SRV record." - } - }, - "port": { - "type": "int", - "metadata": { - "description": "Required. The port value for this SRV record." - } - }, - "target": { - "type": "string", - "metadata": { - "description": "Required. The target domain name for this SRV record." + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "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." + } } } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the SRV record." } }, - "nullable": true, - "metadata": { - "description": "Optional. The list of SRV records in the record set." - } - } - } - }, - "nullable": true - }, - "txtType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", "metadata": { - "description": "Required. The name of the record." + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "srvRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of SRV records in the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } } }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." + "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')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "txtRecords": { - "type": "array", - "items": { - "type": "object", + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "SRV": { + "type": "Microsoft.Network/privateDnsZones/SRV", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", "properties": { - "value": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. The text value of this TXT record." - } - } + "metadata": "[parameters('metadata')]", + "srvRecords": "[parameters('srvRecords')]", + "ttl": "[parameters('ttl')]" } }, - "nullable": true, - "metadata": { - "description": "Optional. The list of TXT records in the record set." - } - } - } - }, - "nullable": true - }, - "virtualNetworkLinkType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "minLength": 1, - "maxLength": 80, - "metadata": { - "description": "Optional. The resource name." - } - }, - "virtualNetworkResourceId": { - "type": "string", - "metadata": { - "description": "Required. The resource ID of the virtual network to link." - } - }, - "location": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The Azure Region where the resource lives." - } - }, - "registrationEnabled": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Is auto-registration of virtual machine records in the virtual network in the Private DNS zone enabled?." - } - }, - "tags": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Resource tags." + "SRV_roleAssignments": { + "copy": { + "name": "SRV_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/SRV/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/SRV', parameters('privateDnsZoneName'), 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": [ + "SRV" + ] } }, - "resolutionPolicy": { - "type": "string", - "allowedValues": [ - "Default", - "NxDomainRedirect" - ], - "nullable": true, - "metadata": { - "description": "Optional. The resolution type of the private-dns-zone fallback machanism." - } - } - } - }, - "nullable": true - } - }, - "parameters": { - "name": { - "type": "string", - "metadata": { - "description": "Required. Private DNS zone name." - } - }, - "a": { - "$ref": "#/definitions/aType", - "metadata": { - "description": "Optional. Array of A records." - } - }, - "aaaa": { - "$ref": "#/definitions/aaaaType", - "metadata": { - "description": "Optional. Array of AAAA records." - } - }, - "cname": { - "$ref": "#/definitions/cnameType", - "metadata": { - "description": "Optional. Array of CNAME records." - } - }, - "mx": { - "$ref": "#/definitions/mxType", - "metadata": { - "description": "Optional. Array of MX records." - } - }, - "ptr": { - "$ref": "#/definitions/ptrType", - "metadata": { - "description": "Optional. Array of PTR records." - } - }, - "soa": { - "$ref": "#/definitions/soaType", - "metadata": { - "description": "Optional. Array of SOA records." - } - }, - "srv": { - "$ref": "#/definitions/srvType", - "metadata": { - "description": "Optional. Array of SRV records." - } - }, - "txt": { - "$ref": "#/definitions/txtType", - "metadata": { - "description": "Optional. Array of TXT records." - } - }, - "virtualNetworkLinks": { - "$ref": "#/definitions/virtualNetworkLinkType", - "metadata": { - "description": "Optional. Array of custom objects describing vNet links of the DNS zone. Each object should contain properties 'virtualNetworkResourceId' and 'registrationEnabled'. The 'vnetResourceId' is a resource ID of a vNet to link, 'registrationEnabled' (bool) enables automatic DNS registration in the zone for the linked vNet." - } - }, - "location": { - "type": "string", - "defaultValue": "global", - "metadata": { - "description": "Optional. The location of the PrivateDNSZone. Should be global." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "tags": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Tags of the resource." - } - }, - "lock": { - "$ref": "#/definitions/lockType", - "metadata": { - "description": "Optional. The lock settings of the service." - } - }, - "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')]", - "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": "2024-03-01", - "name": "[format('46d3xbcp.res.network-privatednszone.{0}.{1}', replace('0.7.0', '.', '-'), 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" + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed SRV record." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed SRV record." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/SRV', parameters('privateDnsZoneName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed SRV record." + }, + "value": "[resourceGroup().name]" } } } - } - }, - "privateDnsZone": { - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('name')]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]" - }, - "privateDnsZone_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/privateDnsZones/{0}', parameters('name'))]", - "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", - "properties": { - "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", - "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" }, "dependsOn": [ "privateDnsZone" ] }, - "privateDnsZone_roleAssignments": { - "copy": { - "name": "privateDnsZone_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}', parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones', 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": [ - "privateDnsZone" - ] - }, - "privateDnsZone_A": { + "privateDnsZone_TXT": { "copy": { - "name": "privateDnsZone_A", - "count": "[length(coalesce(parameters('a'), createArray()))]" + "name": "privateDnsZone_TXT", + "count": "[length(coalesce(parameters('txt'), createArray()))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-ARecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "name": "[format('{0}-PrivateDnsZone-TXTRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -34312,19 +29158,19 @@ "value": "[parameters('name')]" }, "name": { - "value": "[coalesce(parameters('a'), createArray())[copyIndex()].name]" - }, - "aRecords": { - "value": "[tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'aRecords')]" + "value": "[coalesce(parameters('txt'), createArray())[copyIndex()].name]" }, "metadata": { - "value": "[tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'metadata')]" + "value": "[tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'metadata')]" + }, + "txtRecords": { + "value": "[tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'txtRecords')]" }, "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'ttl'), 3600)]" + "value": "[coalesce(tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'ttl'), 3600)]" }, "roleAssignments": { - "value": "[tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'roleAssignments')]" + "value": "[tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'roleAssignments')]" } }, "template": { @@ -34335,10 +29181,10 @@ "_generator": { "name": "bicep", "version": "0.32.4.45862", - "templateHash": "2531120132215940282" + "templateHash": "1855369119498044639" }, - "name": "Private DNS Zone A record", - "description": "This module deploys a Private DNS Zone A record.", + "name": "Private DNS Zone TXT record", + "description": "This module deploys a Private DNS Zone TXT record.", "owner": "Azure/module-maintainers" }, "definitions": { @@ -34426,14 +29272,7 @@ "name": { "type": "string", "metadata": { - "description": "Required. The name of the A record." - } - }, - "aRecords": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. The list of A records in the record set." + "description": "Required. The name of the TXT record." } }, "metadata": { @@ -34450,6 +29289,13 @@ "description": "Optional. The TTL (time-to-live) of the records in the record set." } }, + "txtRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of TXT records in the record set." + } + }, "roleAssignments": { "$ref": "#/definitions/roleAssignmentType", "metadata": { @@ -34482,25 +29328,25 @@ "apiVersion": "2020-06-01", "name": "[parameters('privateDnsZoneName')]" }, - "A": { - "type": "Microsoft.Network/privateDnsZones/A", + "TXT": { + "type": "Microsoft.Network/privateDnsZones/TXT", "apiVersion": "2020-06-01", "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", "properties": { - "aRecords": "[parameters('aRecords')]", "metadata": "[parameters('metadata')]", - "ttl": "[parameters('ttl')]" + "ttl": "[parameters('ttl')]", + "txtRecords": "[parameters('txtRecords')]" } }, - "A_roleAssignments": { + "TXT_roleAssignments": { "copy": { - "name": "A_roleAssignments", + "name": "TXT_roleAssignments", "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/A/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/A', parameters('privateDnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/TXT/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/TXT', parameters('privateDnsZoneName'), 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]", @@ -34511,7 +29357,7 @@ "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" }, "dependsOn": [ - "A" + "TXT" ] } }, @@ -34519,21 +29365,21 @@ "name": { "type": "string", "metadata": { - "description": "The name of the deployed A record." + "description": "The name of the deployed TXT record." }, "value": "[parameters('name')]" }, "resourceId": { "type": "string", "metadata": { - "description": "The resource ID of the deployed A record." + "description": "The resource ID of the deployed TXT record." }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/A', parameters('privateDnsZoneName'), parameters('name'))]" + "value": "[resourceId('Microsoft.Network/privateDnsZones/TXT', parameters('privateDnsZoneName'), parameters('name'))]" }, "resourceGroupName": { "type": "string", "metadata": { - "description": "The resource group of the deployed A record." + "description": "The resource group of the deployed TXT record." }, "value": "[resourceGroup().name]" } @@ -34544,14 +29390,14 @@ "privateDnsZone" ] }, - "privateDnsZone_AAAA": { + "privateDnsZone_virtualNetworkLinks": { "copy": { - "name": "privateDnsZone_AAAA", - "count": "[length(coalesce(parameters('aaaa'), createArray()))]" + "name": "privateDnsZone_virtualNetworkLinks", + "count": "[length(coalesce(parameters('virtualNetworkLinks'), createArray()))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-AAAARecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "name": "[format('{0}-PrivateDnsZone-VirtualNetworkLink-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -34562,19 +29408,22 @@ "value": "[parameters('name')]" }, "name": { - "value": "[coalesce(parameters('aaaa'), createArray())[copyIndex()].name]" + "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'name'), format('{0}-vnetlink', last(split(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()].virtualNetworkResourceId, '/'))))]" }, - "aaaaRecords": { - "value": "[tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'aaaaRecords')]" + "virtualNetworkResourceId": { + "value": "[coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()].virtualNetworkResourceId]" }, - "metadata": { - "value": "[tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'metadata')]" + "location": { + "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'location'), 'global')]" }, - "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'ttl'), 3600)]" + "registrationEnabled": { + "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'registrationEnabled'), false())]" }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'roleAssignments')]" + "tags": { + "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + }, + "resolutionPolicy": { + "value": "[tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'resolutionPolicy')]" } }, "template": { @@ -34585,87 +29434,12 @@ "_generator": { "name": "bicep", "version": "0.32.4.45862", - "templateHash": "16709340450244912125" + "templateHash": "15326596012552051215" }, - "name": "Private DNS Zone AAAA record", - "description": "This module deploys a Private DNS Zone AAAA record.", + "name": "Private DNS Zone Virtual Network Link", + "description": "This module deploys a Private DNS Zone Virtual Network Link.", "owner": "Azure/module-maintainers" }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } - } - } - }, - "nullable": true - } - }, "parameters": { "privateDnsZoneName": { "type": "string", @@ -34675,54 +29449,44 @@ }, "name": { "type": "string", + "defaultValue": "[format('{0}-vnetlink', last(split(parameters('virtualNetworkResourceId'), '/')))]", "metadata": { - "description": "Required. The name of the AAAA record." + "description": "Optional. The name of the virtual network link." } }, - "aaaaRecords": { - "type": "array", - "nullable": true, + "location": { + "type": "string", + "defaultValue": "global", "metadata": { - "description": "Optional. The list of AAAA records in the record set." + "description": "Optional. The location of the PrivateDNSZone. Should be global." } }, - "metadata": { + "tags": { "type": "object", "nullable": true, "metadata": { - "description": "Optional. The metadata attached to the record set." + "description": "Optional. Tags of the resource." } }, - "ttl": { - "type": "int", - "defaultValue": 3600, + "registrationEnabled": { + "type": "bool", + "defaultValue": false, "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." + "description": "Optional. Is auto-registration of virtual machine records in the virtual network in the Private DNS zone enabled?." } }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", + "virtualNetworkResourceId": { + "type": "string", "metadata": { - "description": "Optional. Array of role assignments to create." + "description": "Required. Link to another virtual network resource ID." } - } - }, - "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)))))]" + }, + "resolutionPolicy": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resolution policy on the virtual network link. Only applicable for virtual network links to privatelink zones, and for A,AAAA,CNAME queries. When set to `NxDomainRedirect`, Azure DNS resolver falls back to public resolution if private dns query resolution results in non-existent domain response. `Default` is configured as the default option." } - ], - "builtInRoleNames": { - "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", - "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, "resources": { @@ -34732,60 +29496,49 @@ "apiVersion": "2020-06-01", "name": "[parameters('privateDnsZoneName')]" }, - "AAAA": { - "type": "Microsoft.Network/privateDnsZones/AAAA", - "apiVersion": "2020-06-01", + "virtualNetworkLink": { + "type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks", + "apiVersion": "2024-06-01", "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", "properties": { - "aaaaRecords": "[parameters('aaaaRecords')]", - "metadata": "[parameters('metadata')]", - "ttl": "[parameters('ttl')]" + "registrationEnabled": "[parameters('registrationEnabled')]", + "virtualNetwork": { + "id": "[parameters('virtualNetworkResourceId')]" + }, + "resolutionPolicy": "[parameters('resolutionPolicy')]" } - }, - "AAAA_roleAssignments": { - "copy": { - "name": "AAAA_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/AAAA/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/AAAA', parameters('privateDnsZoneName'), 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": [ - "AAAA" - ] } }, "outputs": { "name": { "type": "string", "metadata": { - "description": "The name of the deployed AAAA record." + "description": "The name of the deployed virtual network link." }, "value": "[parameters('name')]" }, "resourceId": { "type": "string", "metadata": { - "description": "The resource ID of the deployed AAAA record." + "description": "The resource ID of the deployed virtual network link." }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/AAAA', parameters('privateDnsZoneName'), parameters('name'))]" + "value": "[resourceId('Microsoft.Network/privateDnsZones/virtualNetworkLinks', parameters('privateDnsZoneName'), parameters('name'))]" }, "resourceGroupName": { "type": "string", "metadata": { - "description": "The resource group of the deployed AAAA record." + "description": "The resource group of the deployed virtual network link." }, "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('virtualNetworkLink', '2024-06-01', 'full').location]" } } } @@ -34793,2077 +29546,1550 @@ "dependsOn": [ "privateDnsZone" ] + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the private DNS zone was deployed into." + }, + "value": "[resourceGroup().name]" }, - "privateDnsZone_CNAME": { - "copy": { - "name": "privateDnsZone_CNAME", - "count": "[length(coalesce(parameters('cname'), createArray()))]" + "name": { + "type": "string", + "metadata": { + "description": "The name of the private DNS zone." }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-CNAMERecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the private DNS zone." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones', parameters('name'))]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('privateDnsZone', '2020-06-01', 'full').location]" + } + } + } + } + }, + "storageAccount": { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('{0}-storage-account-deployment', variables('nameFormatted')), 64)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[variables('nameFormatted')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "publicNetworkAccess": "[if(parameters('networkIsolation'), createObject('value', 'Disabled'), createObject('value', 'Enabled'))]", + "accessTier": { + "value": "Hot" + }, + "allowBlobPublicAccess": { + "value": false + }, + "allowSharedKeyAccess": { + "value": false + }, + "allowCrossTenantReplication": { + "value": false + }, + "minimumTlsVersion": { + "value": "TLS1_2" + }, + "networkAcls": { + "value": { + "defaultAction": "Allow", + "bypass": "AzureServices" + } + }, + "supportsHttpsTrafficOnly": { + "value": true + }, + "diagnosticSettings": { + "value": [ + { + "workspaceResourceId": "[parameters('logAnalyticsWorkspaceResourceId')]" + } + ] + }, + "privateEndpoints": "[if(parameters('networkIsolation'), createObject('value', createArray(createObject('privateDnsZoneGroup', createObject('privateDnsZoneGroupConfigs', createArray(createObject('privateDnsZoneResourceId', reference('blobPrivateDnsZone').outputs.resourceId.value))), 'service', 'blob', 'subnetResourceId', parameters('virtualNetworkSubnetResourceId')), createObject('privateDnsZoneGroup', createObject('privateDnsZoneGroupConfigs', createArray(createObject('privateDnsZoneResourceId', reference('filePrivateDnsZone').outputs.resourceId.value))), 'service', 'file', 'subnetResourceId', parameters('virtualNetworkSubnetResourceId')))), createObject('value', createArray()))]", + "roleAssignments": { + "value": "[parameters('roleAssignments')]" + } + }, + "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.33.13.18514", + "templateHash": "3537078469744044161" + }, + "name": "Storage Accounts", + "description": "This module deploys a Storage Account." + }, + "definitions": { + "privateEndpointOutputType": { + "type": "object", "properties": { - "expressionEvaluationOptions": { - "scope": "inner" + "name": { + "type": "string", + "metadata": { + "description": "The name of the private endpoint." + } }, - "mode": "Incremental", - "parameters": { - "privateDnsZoneName": { - "value": "[parameters('name')]" - }, - "name": { - "value": "[coalesce(parameters('cname'), createArray())[copyIndex()].name]" - }, - "cnameRecord": { - "value": "[tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'cnameRecord')]" - }, + "resourceId": { + "type": "string", "metadata": { - "value": "[tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'metadata')]" - }, - "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'ttl'), 3600)]" - }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'roleAssignments')]" + "description": "The resource ID of the private endpoint." } }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "languageVersion": "2.0", - "contentVersion": "1.0.0.0", + "groupId": { + "type": "string", + "nullable": true, "metadata": { - "_generator": { - "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "9976020649752073181" - }, - "name": "Private DNS Zone CNAME record", - "description": "This module deploys a Private DNS Zone CNAME record.", - "owner": "Azure/module-maintainers" - }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } - } + "description": "The group Id for the private endpoint Group." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "FQDN that resolves to private endpoint IP address." } }, - "nullable": true - } - }, - "parameters": { - "privateDnsZoneName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the CNAME record." - } - }, - "cnameRecord": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. A CNAME record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata attached to the record set." - } - }, - "ttl": { - "type": "int", - "defaultValue": 3600, - "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - } - }, - "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)))))]" + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "A list of private IP addresses of the private endpoint." + } } - ], - "builtInRoleNames": { - "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", - "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, - "resources": { - "privateDnsZone": { - "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" - }, - "CNAME": { - "type": "Microsoft.Network/privateDnsZones/CNAME", - "apiVersion": "2020-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "properties": { - "cnameRecord": "[parameters('cnameRecord')]", - "metadata": "[parameters('metadata')]", - "ttl": "[parameters('ttl')]" - } - }, - "CNAME_roleAssignments": { - "copy": { - "name": "CNAME_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/CNAME/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/CNAME', parameters('privateDnsZoneName'), 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": [ - "CNAME" - ] - } + "metadata": { + "description": "The custom DNS configurations of the private endpoint." + } + }, + "networkInterfaceResourceIds": { + "type": "array", + "items": { + "type": "string" }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the deployed CNAME record." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the deployed CNAME record." - }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/CNAME', parameters('privateDnsZoneName'), parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group of the deployed CNAME record." - }, - "value": "[resourceGroup().name]" - } + "metadata": { + "description": "The IDs of the network interfaces associated with the private endpoint." } } }, - "dependsOn": [ - "privateDnsZone" - ] + "metadata": { + "__bicep_export!": true + } }, - "privateDnsZone_MX": { - "copy": { - "name": "privateDnsZone_MX", - "count": "[length(coalesce(parameters('mx'), createArray()))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-MXRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "networkAclsType": { + "type": "object", "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "privateDnsZoneName": { - "value": "[parameters('name')]" - }, - "name": { - "value": "[coalesce(parameters('mx'), createArray())[copyIndex()].name]" + "resourceAccessRules": { + "type": "array", + "items": { + "type": "object", + "properties": { + "tenantId": { + "type": "string", + "metadata": { + "description": "Required. The ID of the tenant in which the resource resides in." + } + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the target service. Can also contain a wildcard, if multiple services e.g. in a resource group should be included." + } + } + } }, + "nullable": true, "metadata": { - "value": "[tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'metadata')]" - }, - "mxRecords": { - "value": "[tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'mxRecords')]" - }, - "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'ttl'), 3600)]" - }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'roleAssignments')]" + "description": "Optional. Sets the resource access rules. Array entries must consist of \"tenantId\" and \"resourceId\" fields only." } }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "languageVersion": "2.0", - "contentVersion": "1.0.0.0", + "bypass": { + "type": "string", + "allowedValues": [ + "AzureServices", + "AzureServices, Logging", + "AzureServices, Logging, Metrics", + "AzureServices, Metrics", + "Logging", + "Logging, Metrics", + "Metrics", + "None" + ], + "nullable": true, "metadata": { - "_generator": { - "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "2520323624213076361" - }, - "name": "Private DNS Zone MX record", - "description": "This module deploys a Private DNS Zone MX record.", - "owner": "Azure/module-maintainers" + "description": "Optional. Specifies whether traffic is bypassed for Logging/Metrics/AzureServices. Possible values are any combination of Logging,Metrics,AzureServices (For example, \"Logging, Metrics\"), or None to bypass none of those traffics." + } + }, + "virtualNetworkRules": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. Sets the virtual network rules." + } + }, + "ipRules": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. Sets the IP ACL rules." + } + }, + "defaultAction": { + "type": "string", + "allowedValues": [ + "Allow", + "Deny" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specifies the default action of allow or deny when no other rules match." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "secretsExportConfigurationType": { + "type": "object", + "properties": { + "keyVaultResourceId": { + "type": "string", + "metadata": { + "description": "Required. The key vault name where to store the keys and connection strings generated by the modules." + } + }, + "accessKey1Name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The accessKey1 secret name to create." + } + }, + "connectionString1Name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The connectionString1 secret name to create." + } + }, + "accessKey2Name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The accessKey2 secret name to create." + } + }, + "connectionString2Name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The connectionString2 secret name to create." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "localUserType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the local user used for SFTP Authentication." + } + }, + "hasSharedKey": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Indicates whether shared key exists. Set it to false to remove existing shared key." + } + }, + "hasSshKey": { + "type": "bool", + "metadata": { + "description": "Required. Indicates whether SSH key exists. Set it to false to remove existing SSH key." + } + }, + "hasSshPassword": { + "type": "bool", + "metadata": { + "description": "Required. Indicates whether SSH password exists. Set it to false to remove existing SSH password." + } + }, + "homeDirectory": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The local user home directory." + } + }, + "permissionScopes": { + "type": "array", + "items": { + "$ref": "#/definitions/permissionScopeType" }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } - } - } - }, - "nullable": true - } + "metadata": { + "description": "Required. The permission scopes of the local user." + } + }, + "sshAuthorizedKeys": { + "type": "array", + "items": { + "$ref": "#/definitions/sshAuthorizedKeyType" }, - "parameters": { - "privateDnsZoneName": { + "nullable": true, + "metadata": { + "description": "Optional. The local user SSH authorized keys for SFTP." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "_1.privateEndpointCustomDnsConfigType": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. FQDN that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "_1.privateEndpointIpConfigurationType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource that is unique within a resource group." + } + }, + "properties": { + "type": "object", + "properties": { + "groupId": { "type": "string", "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." } }, - "name": { + "memberName": { "type": "string", "metadata": { - "description": "Required. The name of the MX record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata attached to the record set." - } - }, - "mxRecords": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. The list of MX records in the record set." - } - }, - "ttl": { - "type": "int", - "defaultValue": 3600, - "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to." } }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", + "privateIPAddress": { + "type": "string", "metadata": { - "description": "Optional. Array of role assignments to create." - } - } - }, - "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)))))]" + "description": "Required. A private IP address obtained from the private endpoint's subnet." } - ], - "builtInRoleNames": { - "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", - "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, - "resources": { - "privateDnsZone": { - "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" - }, - "MX": { - "type": "Microsoft.Network/privateDnsZones/MX", - "apiVersion": "2020-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "properties": { - "metadata": "[parameters('metadata')]", - "mxRecords": "[parameters('mxRecords')]", - "ttl": "[parameters('ttl')]" - } - }, - "MX_roleAssignments": { - "copy": { - "name": "MX_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/MX/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/MX', parameters('privateDnsZoneName'), 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')]" + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "_1.privateEndpointPrivateDnsZoneGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS Zone Group config." + } }, - "dependsOn": [ - "MX" - ] + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." + } + } } }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the deployed MX record." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the deployed MX record." - }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/MX', parameters('privateDnsZoneName'), parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group of the deployed MX record." - }, - "value": "[resourceGroup().name]" - } + "metadata": { + "description": "Required. The private DNS Zone Groups to associate the Private Endpoint. A DNS Zone Group can support up to 5 DNS zones." } } }, - "dependsOn": [ - "privateDnsZone" - ] + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } }, - "privateDnsZone_PTR": { - "copy": { - "name": "privateDnsZone_PTR", - "count": "[length(coalesce(parameters('ptr'), createArray()))]" + "_1.secretSetOutputType": { + "type": "object", + "properties": { + "secretResourceId": { + "type": "string", + "metadata": { + "description": "The resourceId of the exported secret." + } + }, + "secretUri": { + "type": "string", + "metadata": { + "description": "The secret URI of the exported secret." + } + }, + "secretUriWithVersion": { + "type": "string", + "metadata": { + "description": "The secret URI with version of the exported secret." + } + } }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-PTRRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "metadata": { + "description": "An AVM-aligned type for the output of the secret set via the secrets export feature.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "customerManagedKeyWithAutoRotateType": { + "type": "object", "properties": { - "expressionEvaluationOptions": { - "scope": "inner" + "keyVaultResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of a key vault to reference a customer managed key for encryption from." + } }, - "mode": "Incremental", - "parameters": { - "privateDnsZoneName": { - "value": "[parameters('name')]" - }, - "name": { - "value": "[coalesce(parameters('ptr'), createArray())[copyIndex()].name]" - }, + "keyName": { + "type": "string", "metadata": { - "value": "[tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'metadata')]" - }, - "ptrRecords": { - "value": "[tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'ptrRecords')]" - }, - "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'ttl'), 3600)]" - }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'roleAssignments')]" + "description": "Required. The name of the customer managed key to use for encryption." } }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "languageVersion": "2.0", - "contentVersion": "1.0.0.0", + "keyVersion": { + "type": "string", + "nullable": true, "metadata": { - "_generator": { - "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "3080404733048745471" - }, - "name": "Private DNS Zone PTR record", - "description": "This module deploys a Private DNS Zone PTR record.", - "owner": "Azure/module-maintainers" - }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } - } + "description": "Optional. The version of the customer managed key to reference for encryption. If not provided, using version as per 'autoRotationEnabled' setting." + } + }, + "autoRotationEnabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable auto-rotating to the latest key version. Default is `true`. If set to `false`, the latest key version at the time of the deployment is used." + } + }, + "userAssignedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a customer-managed key. To be used if the resource type supports auto-rotation of the customer-managed key.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "diagnosticSettingFullType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." } }, - "nullable": true - } - }, - "parameters": { - "privateDnsZoneName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the PTR record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata attached to the record set." - } - }, - "ptrRecords": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. The list of PTR records in the record set." - } - }, - "ttl": { - "type": "int", - "defaultValue": 3600, - "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - } - }, - "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)))))]" + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } } - ], - "builtInRoleNames": { - "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", - "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, - "resources": { - "privateDnsZone": { - "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" - }, - "PTR": { - "type": "Microsoft.Network/privateDnsZones/PTR", - "apiVersion": "2020-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "properties": { - "metadata": "[parameters('metadata')]", - "ptrRecords": "[parameters('ptrRecords')]", - "ttl": "[parameters('ttl')]" - } - }, - "PTR_roleAssignments": { - "copy": { - "name": "PTR_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/PTR/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/PTR', parameters('privateDnsZoneName'), 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')]" + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "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." + } }, - "dependsOn": [ - "PTR" - ] + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } } }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the deployed PTR record." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the deployed PTR record." - }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/PTR', parameters('privateDnsZoneName'), parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group of the deployed PTR record." - }, - "value": "[resourceGroup().name]" - } + "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." } } }, - "dependsOn": [ - "privateDnsZone" - ] + "metadata": { + "description": "An AVM-aligned type for a diagnostic setting. To be used if both logs & metrics are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } }, - "privateDnsZone_SOA": { - "copy": { - "name": "privateDnsZone_SOA", - "count": "[length(coalesce(parameters('soa'), createArray()))]" + "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." + } + } }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-SOARecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "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.5.1" + } + } + }, + "managedIdentityAllType": { + "type": "object", "properties": { - "expressionEvaluationOptions": { - "scope": "inner" + "systemAssigned": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enables system assigned managed identity on the resource." + } }, - "mode": "Incremental", - "parameters": { - "privateDnsZoneName": { - "value": "[parameters('name')]" - }, - "name": { - "value": "[coalesce(parameters('soa'), createArray())[copyIndex()].name]" + "userAssignedResourceIds": { + "type": "array", + "items": { + "type": "string" }, + "nullable": true, "metadata": { - "value": "[tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'metadata')]" - }, - "soaRecord": { - "value": "[tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'soaRecord')]" - }, - "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'ttl'), 3600)]" - }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'roleAssignments')]" + "description": "Optional. The resource ID(s) to assign to the resource. Required if a user assigned identity is used for encryption." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a managed identity configuration. To be used if both a system-assigned & user-assigned identities are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "permissionScopeType": { + "type": "object", + "properties": { + "permissions": { + "type": "string", + "metadata": { + "description": "Required. The permissions for the local user. Possible values include: Read (r), Write (w), Delete (d), List (l), and Create (c)." } }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "languageVersion": "2.0", - "contentVersion": "1.0.0.0", + "resourceName": { + "type": "string", "metadata": { - "_generator": { - "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "6653951445614700931" - }, - "name": "Private DNS Zone SOA record", - "description": "This module deploys a Private DNS Zone SOA record.", - "owner": "Azure/module-maintainers" + "description": "Required. The name of resource, normally the container name or the file share name, used by the local user." + } + }, + "service": { + "type": "string", + "metadata": { + "description": "Required. The service used by the local user, e.g. blob, file." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "local-user/main.bicep" + } + } + }, + "privateEndpointMultiServiceType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private endpoint." + } + }, + "location": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The location to deploy the private endpoint to." + } + }, + "privateLinkServiceConnectionName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private link connection to create." + } + }, + "service": { + "type": "string", + "metadata": { + "description": "Required. The subresource to deploy the private endpoint for. For example \"blob\", \"table\", \"queue\" or \"file\" for a Storage Account's Private Endpoints." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + } + }, + "resourceGroupResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the Resource Group the Private Endpoint will be created in. If not specified, the Resource Group of the provided Virtual Network Subnet is used." + } + }, + "privateDnsZoneGroup": { + "$ref": "#/definitions/_1.privateEndpointPrivateDnsZoneGroupType", + "nullable": true, + "metadata": { + "description": "Optional. The private DNS zone group to configure for the private endpoint." + } + }, + "isManualConnection": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. If Manual Private Link Connection is required." + } + }, + "manualConnectionRequestMessage": { + "type": "string", + "nullable": true, + "maxLength": 140, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with the manual connection request." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/_1.privateEndpointCustomDnsConfigType" }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } - } - } - }, - "nullable": true - } - }, - "parameters": { - "privateDnsZoneName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the SOA record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata attached to the record set." - } - }, - "soaRecord": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. A SOA record." - } - }, - "ttl": { - "type": "int", - "defaultValue": 3600, - "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - } - }, - "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')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", - "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" - } - }, - "resources": { - "privateDnsZone": { - "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" - }, - "SOA": { - "type": "Microsoft.Network/privateDnsZones/SOA", - "apiVersion": "2020-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "properties": { - "metadata": "[parameters('metadata')]", - "soaRecord": "[parameters('soaRecord')]", - "ttl": "[parameters('ttl')]" - } - }, - "SOA_roleAssignments": { - "copy": { - "name": "SOA_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/SOA/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/SOA', parameters('privateDnsZoneName'), 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": [ - "SOA" - ] - } - }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the deployed SOA record." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the deployed SOA record." - }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/SOA', parameters('privateDnsZoneName'), parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group of the deployed SOA record." - }, - "value": "[resourceGroup().name]" - } + "nullable": true, + "metadata": { + "description": "Optional. Custom DNS configurations." } - } - }, - "dependsOn": [ - "privateDnsZone" - ] - }, - "privateDnsZone_SRV": { - "copy": { - "name": "privateDnsZone_SRV", - "count": "[length(coalesce(parameters('srv'), createArray()))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-SRVRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" }, - "mode": "Incremental", - "parameters": { - "privateDnsZoneName": { - "value": "[parameters('name')]" - }, - "name": { - "value": "[coalesce(parameters('srv'), createArray())[copyIndex()].name]" + "ipConfigurations": { + "type": "array", + "items": { + "$ref": "#/definitions/_1.privateEndpointIpConfigurationType" }, + "nullable": true, "metadata": { - "value": "[tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'metadata')]" - }, - "srvRecords": { - "value": "[tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'srvRecords')]" - }, - "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'ttl'), 3600)]" + "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." + } + }, + "applicationSecurityGroupResourceIds": { + "type": "array", + "items": { + "type": "string" }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'roleAssignments')]" + "nullable": true, + "metadata": { + "description": "Optional. Application security groups in which the private endpoint IP configuration is included." } }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "languageVersion": "2.0", - "contentVersion": "1.0.0.0", + "customNetworkInterfaceName": { + "type": "string", + "nullable": true, "metadata": { - "_generator": { - "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "5790774778713328446" - }, - "name": "Private DNS Zone SRV record", - "description": "This module deploys a Private DNS Zone SRV record.", - "owner": "Azure/module-maintainers" - }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } - } - } - }, - "nullable": true - } - }, - "parameters": { - "privateDnsZoneName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the SRV record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata attached to the record set." - } - }, - "srvRecords": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. The list of SRV records in the record set." - } - }, - "ttl": { - "type": "int", - "defaultValue": 3600, - "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - } - }, - "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')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", - "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" - } - }, - "resources": { - "privateDnsZone": { - "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" - }, - "SRV": { - "type": "Microsoft.Network/privateDnsZones/SRV", - "apiVersion": "2020-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "properties": { - "metadata": "[parameters('metadata')]", - "srvRecords": "[parameters('srvRecords')]", - "ttl": "[parameters('ttl')]" - } - }, - "SRV_roleAssignments": { - "copy": { - "name": "SRV_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/SRV/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/SRV', parameters('privateDnsZoneName'), 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": [ - "SRV" - ] - } - }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the deployed SRV record." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the deployed SRV record." - }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/SRV', parameters('privateDnsZoneName'), parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group of the deployed SRV record." - }, - "value": "[resourceGroup().name]" - } + "description": "Optional. The custom name of the network interface attached to the private endpoint." } - } - }, - "dependsOn": [ - "privateDnsZone" - ] - }, - "privateDnsZone_TXT": { - "copy": { - "name": "privateDnsZone_TXT", - "count": "[length(coalesce(parameters('txt'), createArray()))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-TXTRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" }, - "mode": "Incremental", - "parameters": { - "privateDnsZoneName": { - "value": "[parameters('name')]" - }, - "name": { - "value": "[coalesce(parameters('txt'), createArray())[copyIndex()].name]" - }, + "lock": { + "$ref": "#/definitions/lockType", + "nullable": true, "metadata": { - "value": "[tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'metadata')]" - }, - "txtRecords": { - "value": "[tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'txtRecords')]" - }, - "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'ttl'), 3600)]" - }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'roleAssignments')]" + "description": "Optional. Specify the type of lock." } }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "languageVersion": "2.0", - "contentVersion": "1.0.0.0", + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, "metadata": { - "_generator": { - "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "1855369119498044639" - }, - "name": "Private DNS Zone TXT record", - "description": "This module deploys a Private DNS Zone TXT record.", - "owner": "Azure/module-maintainers" - }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } - } - } - }, - "nullable": true - } - }, - "parameters": { - "privateDnsZoneName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the TXT record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata attached to the record set." - } - }, - "ttl": { - "type": "int", - "defaultValue": 3600, - "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." - } - }, - "txtRecords": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. The list of TXT records in the record set." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - } - }, - "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')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", - "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" - } - }, - "resources": { - "privateDnsZone": { - "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" - }, - "TXT": { - "type": "Microsoft.Network/privateDnsZones/TXT", - "apiVersion": "2020-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "properties": { - "metadata": "[parameters('metadata')]", - "ttl": "[parameters('ttl')]", - "txtRecords": "[parameters('txtRecords')]" - } - }, - "TXT_roleAssignments": { - "copy": { - "name": "TXT_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/TXT/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/TXT', parameters('privateDnsZoneName'), 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": [ - "TXT" - ] - } - }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the deployed TXT record." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the deployed TXT record." - }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/TXT', parameters('privateDnsZoneName'), parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group of the deployed TXT record." - }, - "value": "[resourceGroup().name]" - } + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." + } + }, + "enableTelemetry": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." } } }, - "dependsOn": [ - "privateDnsZone" - ] + "metadata": { + "description": "An AVM-aligned type for a private endpoint. To be used if the private endpoint's default service / groupId can NOT be assumed (i.e., for services that have more than one subresource, like Storage Account with Blob (blob, table, queue, file, ...).", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } }, - "privateDnsZone_virtualNetworkLinks": { - "copy": { - "name": "privateDnsZone_virtualNetworkLinks", - "count": "[length(coalesce(parameters('virtualNetworkLinks'), createArray()))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-VirtualNetworkLink-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "roleAssignmentType": { + "type": "object", "properties": { - "expressionEvaluationOptions": { - "scope": "inner" + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } }, - "mode": "Incremental", - "parameters": { - "privateDnsZoneName": { - "value": "[parameters('name')]" - }, - "name": { - "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'name'), format('{0}-vnetlink', last(split(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()].virtualNetworkResourceId, '/'))))]" - }, - "virtualNetworkResourceId": { - "value": "[coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()].virtualNetworkResourceId]" - }, - "location": { - "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'location'), 'global')]" - }, - "registrationEnabled": { - "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'registrationEnabled'), false())]" - }, - "tags": { - "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" - }, - "resolutionPolicy": { - "value": "[tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'resolutionPolicy')]" + "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'." } }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "languageVersion": "2.0", - "contentVersion": "1.0.0.0", + "principalId": { + "type": "string", "metadata": { - "_generator": { - "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "15326596012552051215" - }, - "name": "Private DNS Zone Virtual Network Link", - "description": "This module deploys a Private DNS Zone Virtual Network Link.", - "owner": "Azure/module-maintainers" - }, - "parameters": { - "privateDnsZoneName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", - "defaultValue": "[format('{0}-vnetlink', last(split(parameters('virtualNetworkResourceId'), '/')))]", - "metadata": { - "description": "Optional. The name of the virtual network link." - } - }, - "location": { - "type": "string", - "defaultValue": "global", - "metadata": { - "description": "Optional. The location of the PrivateDNSZone. Should be global." - } - }, - "tags": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Tags of the resource." - } - }, - "registrationEnabled": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "Optional. Is auto-registration of virtual machine records in the virtual network in the Private DNS zone enabled?." - } - }, - "virtualNetworkResourceId": { - "type": "string", - "metadata": { - "description": "Required. Link to another virtual network resource ID." - } - }, - "resolutionPolicy": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The resolution policy on the virtual network link. Only applicable for virtual network links to privatelink zones, and for A,AAAA,CNAME queries. When set to `NxDomainRedirect`, Azure DNS resolver falls back to public resolution if private dns query resolution results in non-existent domain response. `Default` is configured as the default option." - } - } - }, - "resources": { - "privateDnsZone": { - "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" - }, - "virtualNetworkLink": { - "type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks", - "apiVersion": "2024-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", - "properties": { - "registrationEnabled": "[parameters('registrationEnabled')]", - "virtualNetwork": { - "id": "[parameters('virtualNetworkResourceId')]" - }, - "resolutionPolicy": "[parameters('resolutionPolicy')]" - } - } - }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the deployed virtual network link." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the deployed virtual network link." - }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/virtualNetworkLinks', parameters('privateDnsZoneName'), parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group of the deployed virtual network link." - }, - "value": "[resourceGroup().name]" - }, - "location": { - "type": "string", - "metadata": { - "description": "The location the resource was deployed into." - }, - "value": "[reference('virtualNetworkLink', '2024-06-01', 'full').location]" - } + "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." } } }, - "dependsOn": [ - "privateDnsZone" - ] - } - }, - "outputs": { - "resourceGroupName": { - "type": "string", "metadata": { - "description": "The resource group the private DNS zone was deployed into." - }, - "value": "[resourceGroup().name]" + "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.5.1" + } + } }, - "name": { - "type": "string", - "metadata": { - "description": "The name of the private DNS zone." + "secretsOutputType": { + "type": "object", + "properties": {}, + "additionalProperties": { + "$ref": "#/definitions/_1.secretSetOutputType", + "metadata": { + "description": "An exported secret's references." + } }, - "value": "[parameters('name')]" + "metadata": { + "description": "A map of the exported secrets", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } }, - "resourceId": { + "sshAuthorizedKeyType": { + "type": "object", + "properties": { + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Description used to store the function/usage of the key." + } + }, + "key": { + "type": "securestring", + "metadata": { + "description": "Required. SSH public key base64 encoded. The format should be: '{keyType} {keyData}', e.g. ssh-rsa AAAABBBB." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "local-user/main.bicep" + } + } + } + }, + "parameters": { + "name": { "type": "string", + "maxLength": 24, "metadata": { - "description": "The resource ID of the private DNS zone." - }, - "value": "[resourceId('Microsoft.Network/privateDnsZones', parameters('name'))]" + "description": "Required. Name of the Storage Account. Must be lower-case." + } }, "location": { "type": "string", + "defaultValue": "[resourceGroup().location]", "metadata": { - "description": "The location the resource was deployed into." - }, - "value": "[reference('privateDnsZone', '2020-06-01', 'full').location]" - } - } - } - } - }, - "aiServices": { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[take(format('{0}-ai-services-deployment', parameters('name')), 64)]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[format('cog{0}{1}', parameters('name'), parameters('resourceToken'))]" - }, - "location": { - "value": "[parameters('location')]" - }, - "kind": { - "value": "AIServices" - }, - "category": { - "value": "AIServices" - }, - "networkIsolation": { - "value": "[parameters('networkIsolation')]" - }, - "networkAcls": { - "value": "[parameters('networkAcls')]" - }, - "virtualNetworkSubnetResourceId": "[if(parameters('networkIsolation'), createObject('value', parameters('virtualNetworkSubnetResourceId')), createObject('value', ''))]", - "privateDnsZonesResourceIds": "[if(parameters('networkIsolation'), createObject('value', createArray(reference('cognitiveServicesPrivateDnsZone').outputs.resourceId.value, reference('openAiPrivateDnsZone').outputs.resourceId.value)), createObject('value', createArray()))]", - "logAnalyticsWorkspaceResourceId": { - "value": "[parameters('logAnalyticsWorkspaceResourceId')]" - }, - "aiModelDeployments": { - "value": "[parameters('aiModelDeployments')]" - }, - "roleAssignments": "[if(empty(parameters('userObjectId')), createObject('value', createArray()), createObject('value', createArray(createObject('principalId', parameters('userObjectId'), 'principalType', 'User', 'roleDefinitionIdOrName', 'Cognitive Services OpenAI Contributor'), createObject('principalId', parameters('userObjectId'), 'principalType', 'User', 'roleDefinitionIdOrName', 'Cognitive Services Contributor'), createObject('principalId', parameters('userObjectId'), 'principalType', 'User', 'roleDefinitionIdOrName', 'Cognitive Services User'))))]", - "tags": { - "value": "[parameters('tags')]" - } - }, - "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.1.42791", - "templateHash": "2348080591288311162" - } - }, - "definitions": { - "deploymentsType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Specify the name of cognitive service account deployment." - } - }, - "model": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of Cognitive Services account deployment model." - } - }, - "format": { - "type": "string", - "metadata": { - "description": "Required. The format of Cognitive Services account deployment model." - } - }, - "version": { - "type": "string", - "metadata": { - "description": "Required. The version of Cognitive Services account deployment model." - } - } - }, - "metadata": { - "description": "Required. Properties of Cognitive Services account deployment model." - } - }, - "sku": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the resource model definition representing SKU." - } - }, - "capacity": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The capacity of the resource model definition representing SKU." - } - }, - "tier": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The tier of the resource model definition representing SKU." - } - }, - "size": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The size of the resource model definition representing SKU." - } - }, - "family": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The family of the resource model definition representing SKU." - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. The resource model definition representing SKU." - } - }, - "raiPolicyName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of RAI policy." - } - }, - "versionUpgradeOption": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The version upgrade option." - } - } + "description": "Optional. Location for all resources." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" }, + "nullable": true, "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "../customTypes.bicep" - } + "description": "Optional. Array of role assignments to create." } }, - "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." - } - } - }, + "managedIdentities": { + "$ref": "#/definitions/managedIdentityAllType", + "nullable": true, "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.5.1" - } + "description": "Optional. The managed identity definition for this resource." } - } - }, - "parameters": { - "name": { + }, + "kind": { "type": "string", + "defaultValue": "StorageV2", + "allowedValues": [ + "Storage", + "StorageV2", + "BlobStorage", + "FileStorage", + "BlockBlobStorage" + ], "metadata": { - "description": "Name of the Cognitive Services resource. Must be unique in the resource group." + "description": "Optional. Type of Storage Account to create." } }, - "location": { + "skuName": { "type": "string", + "defaultValue": "Standard_GRS", + "allowedValues": [ + "Standard_LRS", + "Standard_GRS", + "Standard_RAGRS", + "Standard_ZRS", + "Premium_LRS", + "Premium_ZRS", + "Standard_GZRS", + "Standard_RAGZRS" + ], "metadata": { - "description": "The location of the Cognitive Services resource." + "description": "Optional. Storage Account Sku Name." } }, - "kind": { + "accessTier": { "type": "string", + "defaultValue": "Hot", "allowedValues": [ - "AIServices", - "AnomalyDetector", - "CognitiveServices", - "ComputerVision", - "ContentModerator", - "ContentSafety", - "ConversationalLanguageUnderstanding", - "CustomVision.Prediction", - "CustomVision.Training", - "Face", - "FormRecognizer", - "HealthInsights", - "ImmersiveReader", - "Internal.AllInOne", - "LUIS", - "LUIS.Authoring", - "LanguageAuthoring", - "MetricsAdvisor", - "OpenAI", - "Personalizer", - "QnAMaker.v2", - "SpeechServices", - "TextAnalytics", - "TextTranslation" + "Premium", + "Hot", + "Cool", + "Cold" ], "metadata": { - "description": "Required. Kind of the Cognitive Services account. Use 'Get-AzCognitiveServicesAccountSku' to determine a valid combinations of 'kind' and 'SKU' for your Azure region." + "description": "Conditional. Required if the Storage Account kind is set to BlobStorage. The access tier is used for billing. The \"Premium\" access tier is the default value for premium block blobs storage account type and it cannot be changed for the premium block blobs storage account type." } }, - "sku": { + "largeFileSharesState": { "type": "string", - "defaultValue": "S0", + "defaultValue": "Disabled", "allowedValues": [ - "S", - "S0", - "S1", - "S2", - "S3", - "S4", - "S5", - "S6", - "S7", - "S8" + "Disabled", + "Enabled" ], "metadata": { - "description": "Required. The SKU of the Cognitive Services account. Use 'Get-AzCognitiveServicesAccountSku' to determine a valid combinations of 'kind' and 'SKU' for your Azure region." + "description": "Optional. Allow large file shares if sets to 'Enabled'. It cannot be disabled once it is enabled. Only supported on locally redundant and zone redundant file shares. It cannot be set on FileStorage storage accounts (storage accounts for premium file shares)." } }, - "category": { - "type": "string", - "defaultValue": "CognitiveService", + "azureFilesIdentityBasedAuthentication": { + "type": "object", + "defaultValue": {}, "metadata": { - "description": "Category of the Cognitive Services account." + "description": "Optional. Provides the identity based authentication settings for Azure Files." } }, - "networkIsolation": { + "defaultToOAuthAuthentication": { "type": "bool", + "defaultValue": false, "metadata": { - "description": "Specifies whether to enable network isolation. If true, the resource will be deployed in a private endpoint and public network access will be disabled." + "description": "Optional. A boolean flag which indicates whether the default authentication is OAuth or not." } }, - "privateDnsZonesResourceIds": { + "allowSharedKeyAccess": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Indicates whether the storage account permits requests to be authorized with the account access key via Shared Key. If false, then all requests, including shared access signatures, must be authorized with Azure Active Directory (Azure AD). The default value is null, which is equivalent to true." + } + }, + "privateEndpoints": { "type": "array", "items": { - "type": "string" + "$ref": "#/definitions/privateEndpointMultiServiceType" }, - "defaultValue": [], + "nullable": true, "metadata": { - "description": "Existing resource ID of the private DNS zone for the private endpoint." + "description": "Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible." } }, - "virtualNetworkSubnetResourceId": { + "managementPolicyRules": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The Storage Account ManagementPolicies Rules." + } + }, + "networkAcls": { + "$ref": "#/definitions/networkAclsType", + "nullable": true, + "metadata": { + "description": "Optional. Networks ACLs, this value contains IPs to whitelist and/or Subnet information. If in use, bypass needs to be supplied. For security reasons, it is recommended to set the DefaultAction Deny." + } + }, + "requireInfrastructureEncryption": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. A Boolean indicating whether or not the service applies a secondary layer of encryption with platform managed keys for data at rest. For security reasons, it is recommended to set it to true." + } + }, + "allowCrossTenantReplication": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Allow or disallow cross AAD tenant object replication." + } + }, + "customDomainName": { "type": "string", + "defaultValue": "", "metadata": { - "description": "Resource ID of the subnet for the private endpoint." + "description": "Optional. Sets the custom domain name assigned to the storage account. Name is the CNAME source." } }, - "logAnalyticsWorkspaceResourceId": { + "customDomainUseSubDomainName": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Indicates whether indirect CName validation is enabled. This should only be set on updates." + } + }, + "dnsEndpointType": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "", + "AzureDnsZone", + "Standard" + ], + "metadata": { + "description": "Optional. Allows you to specify the type of endpoint. Set this to AzureDNSZone to create a large number of accounts in a single subscription, which creates accounts in an Azure DNS Zone and the endpoint URL will have an alphanumeric DNS Zone identifier." + } + }, + "blobServices": { + "type": "object", + "defaultValue": "[if(not(equals(parameters('kind'), 'FileStorage')), createObject('containerDeleteRetentionPolicyEnabled', true(), 'containerDeleteRetentionPolicyDays', 7, 'deleteRetentionPolicyEnabled', true(), 'deleteRetentionPolicyDays', 6), createObject())]", + "metadata": { + "description": "Optional. Blob service and containers to deploy." + } + }, + "fileServices": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. File service and shares to deploy." + } + }, + "queueServices": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Queue service and queues to create." + } + }, + "tableServices": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Table service and tables to create." + } + }, + "allowBlobPublicAccess": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Indicates whether public access is enabled for all blobs or containers in the storage account. For security reasons, it is recommended to set it to false." + } + }, + "minimumTlsVersion": { "type": "string", + "defaultValue": "TLS1_2", + "allowedValues": [ + "TLS1_2", + "TLS1_3" + ], "metadata": { - "description": "The resource ID of the Log Analytics workspace to use for diagnostic settings." + "description": "Optional. Set the minimum TLS version on request to storage. The TLS versions 1.0 and 1.1 are deprecated and not supported anymore." } }, - "aiModelDeployments": { + "enableHierarchicalNamespace": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Conditional. If true, enables Hierarchical Namespace for the storage account. Required if enableSftp or enableNfsV3 is set to true." + } + }, + "enableSftp": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. If true, enables Secure File Transfer Protocol for the storage account. Requires enableHierarchicalNamespace to be true." + } + }, + "localUsers": { "type": "array", "items": { - "$ref": "#/definitions/deploymentsType" + "$ref": "#/definitions/localUserType" }, - "defaultValue": [], + "nullable": true, "metadata": { - "description": "Optional. Specifies the OpenAI deployments to create." + "description": "Optional. Local users to deploy for SFTP authentication." } }, - "roleAssignments": { + "isLocalUserEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Enables local users feature, if set to true." + } + }, + "enableNfsV3": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. If true, enables NFS 3.0 support for the storage account. Requires enableHierarchicalNamespace to be true." + } + }, + "diagnosticSettings": { "type": "array", "items": { - "$ref": "#/definitions/roleAssignmentType" + "$ref": "#/definitions/diagnosticSettingFullType" }, "nullable": true, "metadata": { - "description": "Optional. Array of role assignments to create." + "description": "Optional. The diagnostic settings of the service." } }, - "tags": { - "type": "object", - "defaultValue": {}, + "lock": { + "$ref": "#/definitions/lockType", + "nullable": true, "metadata": { - "description": "Optional. Tags to be applied to the resources." + "description": "Optional. The lock settings of the service." } }, - "networkAcls": { + "tags": { "type": "object", + "nullable": true, "metadata": { - "description": "Optional. A collection of rules governing the accessibility from specific network locations." + "description": "Optional. Tags of the resource." } - } - }, - "variables": { - "copy": [ - { - "name": "privateDnsZones", - "count": "[length(parameters('privateDnsZonesResourceIds'))]", - "input": { - "privateDnsZoneResourceId": "[parameters('privateDnsZonesResourceIds')[copyIndex('privateDnsZones')]]" - } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." } - ], - "nameFormatted": "[take(toLower(parameters('name')), 24)]" - }, - "resources": { - "cognitiveService": { - "type": "Microsoft.Resources/deployments", + }, + "allowedCopyScope": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "", + "AAD", + "PrivateLink" + ], + "metadata": { + "description": "Optional. Restrict copy to and from Storage Accounts within an AAD tenant or with Private Links to the same VNet." + } + }, + "publicNetworkAccess": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "", + "Enabled", + "Disabled" + ], + "metadata": { + "description": "Optional. Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set and networkAcls are not set." + } + }, + "supportsHttpsTrafficOnly": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Allows HTTPS traffic only to storage service if sets to true." + } + }, + "customerManagedKey": { + "$ref": "#/definitions/customerManagedKeyWithAutoRotateType", + "nullable": true, + "metadata": { + "description": "Optional. The customer managed key definition." + } + }, + "sasExpirationPeriod": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The SAS expiration period. DD.HH:MM:SS." + } + }, + "keyType": { + "type": "string", + "nullable": true, + "allowedValues": [ + "Account", + "Service" + ], + "metadata": { + "description": "Optional. The keyType to use with Queue & Table services." + } + }, + "secretsExportConfiguration": { + "$ref": "#/definitions/secretsExportConfigurationType", + "nullable": true, + "metadata": { + "description": "Optional. Key vault reference and secret settings for the module's secrets export." + } + } + }, + "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)))))]" + } + ], + "supportsBlobService": "[or(or(or(equals(parameters('kind'), 'BlockBlobStorage'), equals(parameters('kind'), 'BlobStorage')), equals(parameters('kind'), 'StorageV2')), equals(parameters('kind'), 'Storage'))]", + "supportsFileService": "[or(or(equals(parameters('kind'), 'FileStorage'), equals(parameters('kind'), 'StorageV2')), equals(parameters('kind'), 'Storage'))]", + "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", + "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned,UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', 'None')), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Reader and Data Access": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c12c1c16-33a1-487b-954d-41c89c60f349')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Storage Account Backup Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e5e2a7ff-d759-4cd2-bb51-3152d37e2eb1')]", + "Storage Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab')]", + "Storage Account Key Operator Service Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '81a9662b-bebf-436f-a333-f67b29880f12')]", + "Storage Blob Data Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe')]", + "Storage Blob Data Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b7e6dc6d-f1e8-4753-8033-0f276bb0955b')]", + "Storage Blob Data Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2a2b9908-6ea1-4ae2-8e65-a410df84e7d1')]", + "Storage Blob Delegator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'db58b8e5-c6ad-4a2a-8342-4190687cbf4a')]", + "Storage File Data Privileged Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '69566ab7-960f-475b-8e7c-b3118f30c6bd')]", + "Storage File Data Privileged Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b8eda974-7b85-4f76-af95-65846b26df6d')]", + "Storage File Data SMB Share Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0c867c2a-1d8c-454a-a3db-ab2ea1bdc8bb')]", + "Storage File Data SMB Share Elevated Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a7264617-510b-434b-a828-9731dc254ea7')]", + "Storage File Data SMB Share Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aba4ae5f-2193-4029-9191-0cb91df5e314')]", + "Storage Queue Data Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '974c5e8b-45b9-4653-ba55-5f855dd0fb88')]", + "Storage Queue Data Message Processor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8a0f0c08-91a1-4084-bc3d-661d67233fed')]", + "Storage Queue Data Message Sender": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c6a89b2d-59bc-44d0-9896-0f6e12d7b80a')]", + "Storage Queue Data Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '19e7f393-937e-4f77-808e-94535e297925')]", + "Storage Table Data Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3')]", + "Storage Table Data Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '76199698-9eea-4c19-bc75-cec21354c6b6')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "cMKKeyVault::cMKKey": { + "condition": "[and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), not(empty(tryGet(parameters('customerManagedKey'), 'keyName')))))]", + "existing": true, + "type": "Microsoft.KeyVault/vaults/keys", + "apiVersion": "2023-02-01", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '////'), '/')[4]]", + "name": "[format('{0}/{1}', last(split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), 'dummyVault'), '/')), coalesce(tryGet(parameters('customerManagedKey'), 'keyName'), 'dummyKey'))]" + }, + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.storage-storageaccount.{0}.{1}', replace('0.17.0', '.', '-'), 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" + } + } + } + } + }, + "cMKKeyVault": { + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId')))]", + "existing": true, + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2023-02-01", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '////'), '/')[4]]", + "name": "[last(split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), 'dummyVault'), '/'))]" + }, + "cMKUserAssignedIdentity": { + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId')))]", + "existing": true, + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2023-01-31", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '////'), '/')[4]]", + "name": "[last(split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), 'dummyMsi'), '/'))]" + }, + "storageAccount": { + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2023-05-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "kind": "[parameters('kind')]", + "sku": { + "name": "[parameters('skuName')]" + }, + "identity": "[variables('identity')]", + "tags": "[parameters('tags')]", + "properties": { + "allowSharedKeyAccess": "[parameters('allowSharedKeyAccess')]", + "defaultToOAuthAuthentication": "[parameters('defaultToOAuthAuthentication')]", + "allowCrossTenantReplication": "[parameters('allowCrossTenantReplication')]", + "allowedCopyScope": "[if(not(empty(parameters('allowedCopyScope'))), parameters('allowedCopyScope'), null())]", + "customDomain": { + "name": "[parameters('customDomainName')]", + "useSubDomainName": "[parameters('customDomainUseSubDomainName')]" + }, + "dnsEndpointType": "[if(not(empty(parameters('dnsEndpointType'))), parameters('dnsEndpointType'), null())]", + "isLocalUserEnabled": "[parameters('isLocalUserEnabled')]", + "encryption": "[union(createObject('keySource', if(not(empty(parameters('customerManagedKey'))), 'Microsoft.Keyvault', 'Microsoft.Storage'), 'services', createObject('blob', if(variables('supportsBlobService'), createObject('enabled', true()), null()), 'file', if(variables('supportsFileService'), createObject('enabled', true()), null()), 'table', createObject('enabled', true(), 'keyType', parameters('keyType')), 'queue', createObject('enabled', true(), 'keyType', parameters('keyType'))), 'keyvaultproperties', if(not(empty(parameters('customerManagedKey'))), createObject('keyname', parameters('customerManagedKey').keyName, 'keyvaulturi', reference('cMKKeyVault').vaultUri, 'keyversion', if(not(empty(tryGet(parameters('customerManagedKey'), 'keyVersion'))), parameters('customerManagedKey').keyVersion, if(coalesce(tryGet(parameters('customerManagedKey'), 'autoRotationEnabled'), true()), null(), last(split(reference('cMKKeyVault::cMKKey').keyUriWithVersion, '/'))))), null()), 'identity', createObject('userAssignedIdentity', if(not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'))), extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '//'), '/')[2], split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '////'), '/')[4]), 'Microsoft.ManagedIdentity/userAssignedIdentities', last(split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), 'dummyMsi'), '/'))), null()))), if(parameters('requireInfrastructureEncryption'), createObject('requireInfrastructureEncryption', if(not(equals(parameters('kind'), 'Storage')), parameters('requireInfrastructureEncryption'), null())), createObject()))]", + "accessTier": "[if(and(not(equals(parameters('kind'), 'Storage')), not(equals(parameters('kind'), 'BlockBlobStorage'))), parameters('accessTier'), null())]", + "sasPolicy": "[if(not(empty(parameters('sasExpirationPeriod'))), createObject('expirationAction', 'Log', 'sasExpirationPeriod', parameters('sasExpirationPeriod')), null())]", + "supportsHttpsTrafficOnly": "[parameters('supportsHttpsTrafficOnly')]", + "isHnsEnabled": "[parameters('enableHierarchicalNamespace')]", + "isSftpEnabled": "[parameters('enableSftp')]", + "isNfsV3Enabled": "[if(parameters('enableNfsV3'), parameters('enableNfsV3'), '')]", + "largeFileSharesState": "[if(or(equals(parameters('skuName'), 'Standard_LRS'), equals(parameters('skuName'), 'Standard_ZRS')), parameters('largeFileSharesState'), null())]", + "minimumTlsVersion": "[parameters('minimumTlsVersion')]", + "networkAcls": "[if(not(empty(parameters('networkAcls'))), union(createObject('resourceAccessRules', tryGet(parameters('networkAcls'), 'resourceAccessRules'), 'defaultAction', coalesce(tryGet(parameters('networkAcls'), 'defaultAction'), 'Deny'), 'virtualNetworkRules', tryGet(parameters('networkAcls'), 'virtualNetworkRules'), 'ipRules', tryGet(parameters('networkAcls'), 'ipRules')), if(contains(parameters('networkAcls'), 'bypass'), createObject('bypass', tryGet(parameters('networkAcls'), 'bypass')), createObject())), createObject('bypass', 'AzureServices', 'defaultAction', 'Deny'))]", + "allowBlobPublicAccess": "[parameters('allowBlobPublicAccess')]", + "publicNetworkAccess": "[if(not(empty(parameters('publicNetworkAccess'))), parameters('publicNetworkAccess'), if(and(not(empty(parameters('privateEndpoints'))), empty(parameters('networkAcls'))), 'Disabled', null()))]", + "azureFilesIdentityBasedAuthentication": "[if(not(empty(parameters('azureFilesIdentityBasedAuthentication'))), parameters('azureFilesIdentityBasedAuthentication'), null())]" + }, + "dependsOn": [ + "cMKKeyVault::cMKKey", + "cMKKeyVault" + ] + }, + "storageAccount_diagnosticSettings": { + "copy": { + "name": "storageAccount_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "storageAccount" + ] + }, + "storageAccount_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.Storage/storageAccounts/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "storageAccount" + ] + }, + "storageAccount_roleAssignments": { + "copy": { + "name": "storageAccount_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Storage/storageAccounts', 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": [ + "storageAccount" + ] + }, + "storageAccount_privateEndpoints": { + "copy": { + "name": "storageAccount_privateEndpoints", + "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[take(format('cog-{0}-{1}-deployment', parameters('kind'), parameters('name')), 64)]", + "name": "[format('{0}-storageAccount-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -36871,52 +31097,43 @@ "mode": "Incremental", "parameters": { "name": { - "value": "[variables('nameFormatted')]" - }, - "location": { - "value": "[parameters('location')]" - }, - "tags": { - "value": "[parameters('tags')]" + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'name'), format('pep-{0}-{1}-{2}', last(split(resourceId('Microsoft.Storage/storageAccounts', parameters('name')), '/')), coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service, copyIndex()))]" }, - "sku": { - "value": "[parameters('sku')]" + "privateLinkServiceConnections": "[if(not(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true())), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.Storage/storageAccounts', parameters('name')), '/')), coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service, copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.Storage/storageAccounts', parameters('name')), 'groupIds', createArray(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service))))), createObject('value', null()))]", + "manualPrivateLinkServiceConnections": "[if(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true()), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.Storage/storageAccounts', parameters('name')), '/')), coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service, copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.Storage/storageAccounts', parameters('name')), 'groupIds', createArray(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service), 'requestMessage', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualConnectionRequestMessage'), 'Manual approval required.'))))), createObject('value', null()))]", + "subnetResourceId": { + "value": "[coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId]" }, - "kind": { - "value": "[parameters('kind')]" + "enableTelemetry": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'enableTelemetry'), parameters('enableTelemetry'))]" }, - "allowProjectManagement": { - "value": true + "location": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'location'), reference(split(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location)]" }, - "managedIdentities": { - "value": { - "systemAssigned": true - } + "lock": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" }, - "deployments": { - "value": "[parameters('aiModelDeployments')]" + "privateDnsZoneGroup": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroup')]" }, - "customSubDomainName": { - "value": "[parameters('name')]" + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'roleAssignments')]" }, - "disableLocalAuth": { - "value": "[parameters('networkIsolation')]" + "tags": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" }, - "publicNetworkAccess": "[if(parameters('networkIsolation'), createObject('value', 'Disabled'), createObject('value', 'Enabled'))]", - "diagnosticSettings": { - "value": [ - { - "workspaceResourceId": "[parameters('logAnalyticsWorkspaceResourceId')]" - } - ] + "customDnsConfigs": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customDnsConfigs')]" }, - "roleAssignments": { - "value": "[parameters('roleAssignments')]" + "ipConfigurations": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'ipConfigurations')]" }, - "networkAcls": { - "value": "[parameters('networkAcls')]" + "applicationSecurityGroupResourceIds": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'applicationSecurityGroupResourceIds')]" }, - "privateEndpoints": "[if(parameters('networkIsolation'), createObject('value', createArray(createObject('privateDnsZoneGroup', createObject('privateDnsZoneGroupConfigs', variables('privateDnsZones')), 'subnetResourceId', parameters('virtualNetworkSubnetResourceId')))), createObject('value', createArray()))]" + "customNetworkInterfaceName": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customNetworkInterfaceName')]" + } }, "template": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", @@ -36925,722 +31142,195 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "16135659971302525380" + "version": "0.33.13.18514", + "templateHash": "15954548978129725136" }, - "name": "Cognitive Services", - "description": "This module deploys a Cognitive Service." + "name": "Private Endpoints", + "description": "This module deploys a Private Endpoint." }, "definitions": { - "privateEndpointOutputType": { + "privateDnsZoneGroupType": { "type": "object", "properties": { "name": { - "type": "string", - "metadata": { - "description": "The name of the private endpoint." - } - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the private endpoint." - } - }, - "groupId": { "type": "string", "nullable": true, "metadata": { - "description": "The group Id for the private endpoint Group." - } - }, - "customDnsConfigs": { - "type": "array", - "items": { - "type": "object", - "properties": { - "fqdn": { - "type": "string", - "nullable": true, - "metadata": { - "description": "FQDN that resolves to private endpoint IP address." - } - }, - "ipAddresses": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "A list of private IP addresses of the private endpoint." - } - } - } - }, - "metadata": { - "description": "The custom DNS configurations of the private endpoint." + "description": "Optional. The name of the Private DNS Zone Group." } }, - "networkInterfaceResourceIds": { + "privateDnsZoneGroupConfigs": { "type": "array", "items": { - "type": "string" + "$ref": "#/definitions/privateDnsZoneGroupConfigType" }, "metadata": { - "description": "The IDs of the network interfaces associated with the private endpoint." + "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." } } }, "metadata": { - "__bicep_export!": true, - "description": "The type for the private endpoint output." + "__bicep_export!": true } }, - "deploymentType": { + "ipConfigurationType": { "type": "object", "properties": { "name": { "type": "string", - "nullable": true, "metadata": { - "description": "Optional. Specify the name of cognitive service account deployment." + "description": "Required. The name of the resource that is unique within a resource group." } }, - "model": { + "properties": { "type": "object", "properties": { - "name": { + "groupId": { "type": "string", "metadata": { - "description": "Required. The name of Cognitive Services account deployment model." + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." } }, - "format": { + "memberName": { "type": "string", "metadata": { - "description": "Required. The format of Cognitive Services account deployment model." + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." } }, - "version": { + "privateIPAddress": { "type": "string", "metadata": { - "description": "Required. The version of Cognitive Services account deployment model." + "description": "Required. A private IP address obtained from the private endpoint's subnet." } } }, "metadata": { - "description": "Required. Properties of Cognitive Services account deployment model." + "description": "Required. Properties of private endpoint IP configurations." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "privateLinkServiceConnectionType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the private link service connection." } }, - "sku": { + "properties": { "type": "object", "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the resource model definition representing SKU." - } - }, - "capacity": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The capacity of the resource model definition representing SKU." - } - }, - "tier": { - "type": "string", - "nullable": true, + "groupIds": { + "type": "array", + "items": { + "type": "string" + }, "metadata": { - "description": "Optional. The tier of the resource model definition representing SKU." + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." } }, - "size": { + "privateLinkServiceId": { "type": "string", - "nullable": true, "metadata": { - "description": "Optional. The size of the resource model definition representing SKU." + "description": "Required. The resource id of private link service." } }, - "family": { + "requestMessage": { "type": "string", "nullable": true, "metadata": { - "description": "Optional. The family of the resource model definition representing SKU." + "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." } } }, - "nullable": true, "metadata": { - "description": "Optional. The resource model definition representing SKU." + "description": "Required. Properties of private link service connection." } - }, - "raiPolicyName": { + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "customDnsConfigType": { + "type": "object", + "properties": { + "fqdn": { "type": "string", "nullable": true, "metadata": { - "description": "Optional. The name of RAI policy." + "description": "Optional. FQDN that resolves to private endpoint IP address." } }, - "versionUpgradeOption": { - "type": "string", - "nullable": true, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, "metadata": { - "description": "Optional. The version upgrade option." + "description": "Required. A list of private IP addresses of the private endpoint." } } }, "metadata": { - "__bicep_export!": true, - "description": "The type for a cognitive services account deployment." + "__bicep_export!": true } }, - "endpointType": { + "lockType": { "type": "object", "properties": { "name": { "type": "string", "nullable": true, "metadata": { - "description": "Type of the endpoint." + "description": "Optional. Specify the name of lock." } }, - "endpoint": { + "kind": { "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], "nullable": true, "metadata": { - "description": "The endpoint URI." + "description": "Optional. Specify the type of lock." } } }, "metadata": { - "__bicep_export!": true, - "description": "The type for a cognitive services account endpoint." + "description": "An AVM-aligned type for a lock.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } } }, - "secretsExportConfigurationType": { + "privateDnsZoneGroupConfigType": { "type": "object", "properties": { - "keyVaultResourceId": { - "type": "string", - "metadata": { - "description": "Required. The key vault name where to store the keys and connection strings generated by the modules." - } - }, - "accessKey1Name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name for the accessKey1 secret to create." - } - }, - "accessKey2Name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name for the accessKey2 secret to create." - } - } - }, - "metadata": { - "__bicep_export!": true, - "description": "The type of the secrets exported to the provided Key Vault." - } - }, - "_1.privateEndpointCustomDnsConfigType": { - "type": "object", - "properties": { - "fqdn": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. FQDN that resolves to private endpoint IP address." - } - }, - "ipAddresses": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. A list of private IP addresses of the private endpoint." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } - } - }, - "_1.privateEndpointIpConfigurationType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the resource that is unique within a resource group." - } - }, - "properties": { - "type": "object", - "properties": { - "groupId": { - "type": "string", - "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." - } - }, - "memberName": { - "type": "string", - "metadata": { - "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to." - } - }, - "privateIPAddress": { - "type": "string", - "metadata": { - "description": "Required. A private IP address obtained from the private endpoint's subnet." - } - } - }, - "metadata": { - "description": "Required. Properties of private endpoint IP configurations." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } - } - }, - "_1.privateEndpointPrivateDnsZoneGroupType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the Private DNS Zone Group." - } - }, - "privateDnsZoneGroupConfigs": { - "type": "array", - "items": { - "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": "Required. The private DNS Zone Groups to associate the Private Endpoint. A DNS Zone Group can support up to 5 DNS zones." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } - } - }, - "_1.secretSetOutputType": { - "type": "object", - "properties": { - "secretResourceId": { - "type": "string", - "metadata": { - "description": "The resourceId of the exported secret." - } - }, - "secretUri": { - "type": "string", - "metadata": { - "description": "The secret URI of the exported secret." - } - }, - "secretUriWithVersion": { - "type": "string", - "metadata": { - "description": "The secret URI with version of the exported secret." - } - } - }, - "metadata": { - "description": "An AVM-aligned type for the output of the secret set via the secrets export feature.", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } - } - }, - "customerManagedKeyType": { - "type": "object", - "properties": { - "keyVaultResourceId": { - "type": "string", - "metadata": { - "description": "Required. The resource ID of a key vault to reference a customer managed key for encryption from." - } - }, - "keyName": { - "type": "string", - "metadata": { - "description": "Required. The name of the customer managed key to use for encryption." - } - }, - "keyVersion": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The version of the customer managed key to reference for encryption. If not provided, the deployment will use the latest version available at deployment time." - } - }, - "userAssignedIdentityResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use." - } - } - }, - "metadata": { - "description": "An AVM-aligned type for a customer-managed key. To be used if the resource type does not support auto-rotation of the customer-managed key.", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } - } - }, - "diagnosticSettingFullType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the diagnostic setting." - } - }, - "logCategoriesAndGroups": { - "type": "array", - "items": { - "type": "object", - "properties": { - "category": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." - } - }, - "categoryGroup": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." - } - }, - "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 logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." - } - }, - "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 both logs & metrics are supported by the resource provider.", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } - } - }, - "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." - } - } - }, - "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.5.1" - } - } - }, - "managedIdentityAllType": { - "type": "object", - "properties": { - "systemAssigned": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Enables system assigned managed identity on the resource." - } - }, - "userAssignedResourceIds": { - "type": "array", - "items": { - "type": "string" - }, - "nullable": true, - "metadata": { - "description": "Optional. The resource ID(s) to assign to the resource. Required if a user assigned identity is used for encryption." - } - } - }, - "metadata": { - "description": "An AVM-aligned type for a managed identity configuration. To be used if both a system-assigned & user-assigned identities are supported by the resource provider.", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } - } - }, - "privateEndpointSingleServiceType": { - "type": "object", - "properties": { - "name": { + "name": { "type": "string", "nullable": true, "metadata": { - "description": "Optional. The name of the Private Endpoint." - } - }, - "location": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The location to deploy the Private Endpoint to." - } - }, - "privateLinkServiceConnectionName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the private link connection to create." - } - }, - "service": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The subresource to deploy the Private Endpoint for. For example \"vault\" for a Key Vault Private Endpoint." - } - }, - "subnetResourceId": { - "type": "string", - "metadata": { - "description": "Required. Resource ID of the subnet where the endpoint needs to be created." - } - }, - "resourceGroupResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The resource ID of the Resource Group the Private Endpoint will be created in. If not specified, the Resource Group of the provided Virtual Network Subnet is used." - } - }, - "privateDnsZoneGroup": { - "$ref": "#/definitions/_1.privateEndpointPrivateDnsZoneGroupType", - "nullable": true, - "metadata": { - "description": "Optional. The private DNS Zone Group to configure for the Private Endpoint." - } - }, - "isManualConnection": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. If Manual Private Link Connection is required." - } - }, - "manualConnectionRequestMessage": { - "type": "string", - "nullable": true, - "maxLength": 140, - "metadata": { - "description": "Optional. A message passed to the owner of the remote resource with the manual connection request." - } - }, - "customDnsConfigs": { - "type": "array", - "items": { - "$ref": "#/definitions/_1.privateEndpointCustomDnsConfigType" - }, - "nullable": true, - "metadata": { - "description": "Optional. Custom DNS configurations." - } - }, - "ipConfigurations": { - "type": "array", - "items": { - "$ref": "#/definitions/_1.privateEndpointIpConfigurationType" - }, - "nullable": true, - "metadata": { - "description": "Optional. A list of IP configurations of the Private Endpoint. This will be used to map to the first-party Service endpoints." - } - }, - "applicationSecurityGroupResourceIds": { - "type": "array", - "items": { - "type": "string" - }, - "nullable": true, - "metadata": { - "description": "Optional. Application security groups in which the Private Endpoint IP configuration is included." + "description": "Optional. The name of the private DNS zone group config." } }, - "customNetworkInterfaceName": { + "privateDnsZoneResourceId": { "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The custom name of the network interface attached to the Private Endpoint." - } - }, - "lock": { - "$ref": "#/definitions/lockType", - "nullable": true, - "metadata": { - "description": "Optional. Specify the type of lock." - } - }, - "roleAssignments": { - "type": "array", - "items": { - "$ref": "#/definitions/roleAssignmentType" - }, - "nullable": true, - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "tags": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Tags to be applied on all resources/Resource Groups in this deployment." - } - }, - "enableTelemetry": { - "type": "bool", - "nullable": true, "metadata": { - "description": "Optional. Enable/Disable usage telemetry for module." + "description": "Required. The resource id of the private DNS zone." } } }, "metadata": { - "description": "An AVM-aligned type for a private endpoint. To be used if the private endpoint's default service / groupId can be assumed (i.e., for services that only have one Private Endpoint type like 'vault' for key vault).", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + "sourceTemplate": "private-dns-zone-group/main.bicep" } } }, @@ -37718,139 +31408,60 @@ "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } - }, - "secretsOutputType": { - "type": "object", - "properties": {}, - "additionalProperties": { - "$ref": "#/definitions/_1.secretSetOutputType", - "metadata": { - "description": "An exported secret's references." - } - }, - "metadata": { - "description": "A map of the exported secrets", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } - } } }, "parameters": { "name": { "type": "string", "metadata": { - "description": "Required. The name of Cognitive Services account." - } - }, - "kind": { - "type": "string", - "allowedValues": [ - "AIServices", - "AnomalyDetector", - "CognitiveServices", - "ComputerVision", - "ContentModerator", - "ContentSafety", - "ConversationalLanguageUnderstanding", - "CustomVision.Prediction", - "CustomVision.Training", - "Face", - "FormRecognizer", - "HealthInsights", - "ImmersiveReader", - "Internal.AllInOne", - "LUIS", - "LUIS.Authoring", - "LanguageAuthoring", - "MetricsAdvisor", - "OpenAI", - "Personalizer", - "QnAMaker.v2", - "SpeechServices", - "TextAnalytics", - "TextTranslation" - ], - "metadata": { - "description": "Required. Kind of the Cognitive Services account. Use 'Get-AzCognitiveServicesAccountSku' to determine a valid combinations of 'kind' and 'SKU' for your Azure region." - } - }, - "sku": { - "type": "string", - "defaultValue": "S0", - "allowedValues": [ - "C2", - "C3", - "C4", - "F0", - "F1", - "S", - "S0", - "S1", - "S10", - "S2", - "S3", - "S4", - "S5", - "S6", - "S7", - "S8", - "S9" - ], - "metadata": { - "description": "Optional. SKU of the Cognitive Services account. Use 'Get-AzCognitiveServicesAccountSku' to determine a valid combinations of 'kind' and 'SKU' for your Azure region." + "description": "Required. Name of the private endpoint resource to create." } }, - "location": { + "subnetResourceId": { "type": "string", - "defaultValue": "[resourceGroup().location]", "metadata": { - "description": "Optional. Location for all Resources." + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." } }, - "diagnosticSettings": { + "applicationSecurityGroupResourceIds": { "type": "array", "items": { - "$ref": "#/definitions/diagnosticSettingFullType" + "type": "string" }, "nullable": true, "metadata": { - "description": "Optional. The diagnostic settings of the service." + "description": "Optional. Application security groups in which the private endpoint IP configuration is included." } }, - "publicNetworkAccess": { + "customNetworkInterfaceName": { "type": "string", "nullable": true, - "allowedValues": [ - "Enabled", - "Disabled" - ], "metadata": { - "description": "Optional. Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set and networkAcls are not set." + "description": "Optional. The custom name of the network interface attached to the private endpoint." } }, - "customSubDomainName": { - "type": "string", + "ipConfigurations": { + "type": "array", + "items": { + "$ref": "#/definitions/ipConfigurationType" + }, "nullable": true, "metadata": { - "description": "Conditional. Subdomain name used for token-based authentication. Required if 'networkAcls' or 'privateEndpoints' are set." + "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." } }, - "networkAcls": { - "type": "object", + "privateDnsZoneGroup": { + "$ref": "#/definitions/privateDnsZoneGroupType", "nullable": true, "metadata": { - "description": "Optional. A collection of rules governing the accessibility from specific network locations." + "description": "Optional. The private DNS zone group to configure for the private endpoint." } }, - "privateEndpoints": { - "type": "array", - "items": { - "$ref": "#/definitions/privateEndpointSingleServiceType" - }, - "nullable": true, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", "metadata": { - "description": "Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible." + "description": "Optional. Location for all Resources." } }, "lock": { @@ -37874,108 +31485,44 @@ "type": "object", "nullable": true, "metadata": { - "description": "Optional. Tags of the resource." + "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." } }, - "allowedFqdnList": { + "customDnsConfigs": { "type": "array", + "items": { + "$ref": "#/definitions/customDnsConfigType" + }, "nullable": true, "metadata": { - "description": "Optional. List of allowed FQDN." - } - }, - "apiProperties": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The API properties for special APIs." - } - }, - "disableLocalAuth": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Optional. Allow only Azure AD authentication. Should be enabled for security reasons." - } - }, - "customerManagedKey": { - "$ref": "#/definitions/customerManagedKeyType", - "nullable": true, - "metadata": { - "description": "Optional. The customer managed key definition." - } - }, - "dynamicThrottlingEnabled": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "Optional. The flag to enable dynamic throttling." - } - }, - "migrationToken": { - "type": "securestring", - "nullable": true, - "metadata": { - "description": "Optional. Resource migration token." - } - }, - "restore": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "Optional. Restore a soft-deleted cognitive service at deployment time. Will fail if no such soft-deleted resource exists." - } - }, - "restrictOutboundNetworkAccess": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Optional. Restrict outbound network access." + "description": "Optional. Custom DNS configurations." } }, - "userOwnedStorage": { + "manualPrivateLinkServiceConnections": { "type": "array", + "items": { + "$ref": "#/definitions/privateLinkServiceConnectionType" + }, "nullable": true, "metadata": { - "description": "Optional. The storage accounts for this resource." - } - }, - "managedIdentities": { - "$ref": "#/definitions/managedIdentityAllType", - "nullable": true, - "metadata": { - "description": "Optional. The managed identity definition for this resource." - } - }, - "enableTelemetry": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Optional. Enable/Disable usage telemetry for module." + "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." } }, - "deployments": { + "privateLinkServiceConnections": { "type": "array", "items": { - "$ref": "#/definitions/deploymentType" + "$ref": "#/definitions/privateLinkServiceConnectionType" }, "nullable": true, "metadata": { - "description": "Optional. Array of deployments about cognitive service accounts to create." - } - }, - "secretsExportConfiguration": { - "$ref": "#/definitions/secretsExportConfigurationType", - "nullable": true, - "metadata": { - "description": "Optional. Key vault reference and secret settings for the module's secrets export." + "description": "Conditional. A grouping of information about the connection to the remote resource. Required if `manualPrivateLinkServiceConnections` is empty." } }, - "allowProjectManagement": { + "enableTelemetry": { "type": "bool", - "nullable": true, + "defaultValue": true, "metadata": { - "description": "Optional. Enable/Disable project management feature for AI Foundry." + "description": "Optional. Enable/Disable usage telemetry for module." } } }, @@ -37987,57 +31534,25 @@ "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)))))]" } ], - "enableReferencedModulesTelemetry": false, - "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", - "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned, UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', null())), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", "builtInRoleNames": { - "Cognitive Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '25fbc0a9-bd7c-42a3-aa1a-3b75d497ee68')]", - "Cognitive Services Custom Vision Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c1ff6cc2-c111-46fe-8896-e0ef812ad9f3')]", - "Cognitive Services Custom Vision Deployment": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5c4089e1-6d96-4d2f-b296-c1bc7137275f')]", - "Cognitive Services Custom Vision Labeler": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '88424f51-ebe7-446f-bc41-7fa16989e96c')]", - "Cognitive Services Custom Vision Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '93586559-c37d-4a6b-ba08-b9f0940c2d73')]", - "Cognitive Services Custom Vision Trainer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0a5ae4ab-0d65-4eeb-be61-29fc9b54394b')]", - "Cognitive Services Data Reader (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b59867f0-fa02-499b-be73-45a86b5b3e1c')]", - "Cognitive Services Face Recognizer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9894cab4-e18a-44aa-828b-cb588cd6f2d7')]", - "Cognitive Services Immersive Reader User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b2de6794-95db-4659-8781-7e080d3f2b9d')]", - "Cognitive Services Language Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f07febfe-79bc-46b1-8b37-790e26e6e498')]", - "Cognitive Services Language Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7628b7b8-a8b2-4cdc-b46f-e9b35248918e')]", - "Cognitive Services Language Writer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f2310ca1-dc64-4889-bb49-c8e0fa3d47a8')]", - "Cognitive Services LUIS Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f72c8140-2111-481c-87ff-72b910f6e3f8')]", - "Cognitive Services LUIS Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18e81cdc-4e98-4e29-a639-e7d10c5a6226')]", - "Cognitive Services LUIS Writer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6322a993-d5c9-4bed-b113-e49bbea25b27')]", - "Cognitive Services Metrics Advisor Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'cb43c632-a144-4ec5-977c-e80c4affc34a')]", - "Cognitive Services Metrics Advisor User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3b20f47b-3825-43cb-8114-4bd2201156a8')]", - "Cognitive Services OpenAI Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a001fd3d-188f-4b5d-821b-7da978bf7442')]", - "Cognitive Services OpenAI User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e0bd9bd-7b93-4f28-af87-19fc36ad61bd')]", - "Cognitive Services QnA Maker Editor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f4cc2bf9-21be-47a1-bdf1-5c5804381025')]", - "Cognitive Services QnA Maker Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '466ccd10-b268-4a11-b098-b4849f024126')]", - "Cognitive Services Speech Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0e75ca1e-0464-4b4d-8b93-68208a576181')]", - "Cognitive Services Speech User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f2dc8367-1007-4938-bd23-fe263f013447')]", - "Cognitive Services User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a97b65f3-24c7-4388-baec-2e87135dc908')]", - "Azure AI Developer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '64702f94-c441-49e6-a78b-ef80e0188fee')]", "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')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" } }, "resources": { - "cMKKeyVault::cMKKey": { - "condition": "[and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), not(empty(tryGet(parameters('customerManagedKey'), 'keyName')))))]", - "existing": true, - "type": "Microsoft.KeyVault/vaults/keys", - "apiVersion": "2023-07-01", - "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[2]]", - "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[4]]", - "name": "[format('{0}/{1}', last(split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')), tryGet(parameters('customerManagedKey'), 'keyName'))]" - }, "avmTelemetry": { "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2024-03-01", - "name": "[format('46d3xbcp.res.cognitiveservices-account.{0}.{1}', replace('0.11.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.10.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -38053,140 +31568,55 @@ } } }, - "cMKKeyVault": { - "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId')))]", - "existing": true, - "type": "Microsoft.KeyVault/vaults", - "apiVersion": "2023-07-01", - "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[2]]", - "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[4]]", - "name": "[last(split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/'))]" - }, - "cMKUserAssignedIdentity": { - "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId')))]", - "existing": true, - "type": "Microsoft.ManagedIdentity/userAssignedIdentities", - "apiVersion": "2025-01-31-preview", - "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/')[2]]", - "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/')[4]]", - "name": "[last(split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/'))]" - }, - "cognitiveService": { - "type": "Microsoft.CognitiveServices/accounts", - "apiVersion": "2025-04-01-preview", + "privateEndpoint": { + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-11-01", "name": "[parameters('name')]", - "kind": "[parameters('kind')]", - "identity": "[variables('identity')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", - "sku": { - "name": "[parameters('sku')]" - }, - "properties": { - "allowProjectManagement": "[parameters('allowProjectManagement')]", - "customSubDomainName": "[parameters('customSubDomainName')]", - "networkAcls": "[if(not(empty(coalesce(parameters('networkAcls'), createObject()))), createObject('defaultAction', tryGet(parameters('networkAcls'), 'defaultAction'), 'virtualNetworkRules', coalesce(tryGet(parameters('networkAcls'), 'virtualNetworkRules'), createArray()), 'ipRules', coalesce(tryGet(parameters('networkAcls'), 'ipRules'), createArray())), null())]", - "publicNetworkAccess": "[if(not(equals(parameters('publicNetworkAccess'), null())), parameters('publicNetworkAccess'), if(not(empty(parameters('networkAcls'))), 'Enabled', 'Disabled'))]", - "allowedFqdnList": "[parameters('allowedFqdnList')]", - "apiProperties": "[parameters('apiProperties')]", - "disableLocalAuth": "[parameters('disableLocalAuth')]", - "encryption": "[if(not(empty(parameters('customerManagedKey'))), createObject('keySource', 'Microsoft.KeyVault', 'keyVaultProperties', createObject('identityClientId', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), ''))), reference('cMKUserAssignedIdentity').clientId, null()), 'keyVaultUri', reference('cMKKeyVault').vaultUri, 'keyName', parameters('customerManagedKey').keyName, 'keyVersion', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'keyVersion'), ''))), tryGet(parameters('customerManagedKey'), 'keyVersion'), last(split(reference('cMKKeyVault::cMKKey').keyUriWithVersion, '/'))))), null())]", - "migrationToken": "[parameters('migrationToken')]", - "restore": "[parameters('restore')]", - "restrictOutboundNetworkAccess": "[parameters('restrictOutboundNetworkAccess')]", - "userOwnedStorage": "[parameters('userOwnedStorage')]", - "dynamicThrottlingEnabled": "[parameters('dynamicThrottlingEnabled')]" - }, - "dependsOn": [ - "cMKKeyVault", - "cMKKeyVault::cMKKey", - "cMKUserAssignedIdentity" - ] - }, - "cognitiveService_deployments": { - "copy": { - "name": "cognitiveService_deployments", - "count": "[length(coalesce(parameters('deployments'), createArray()))]", - "mode": "serial", - "batchSize": 1 - }, - "type": "Microsoft.CognitiveServices/accounts/deployments", - "apiVersion": "2025-04-01-preview", - "name": "[format('{0}/{1}', parameters('name'), coalesce(tryGet(coalesce(parameters('deployments'), createArray())[copyIndex()], 'name'), format('{0}-deployments', parameters('name'))))]", "properties": { - "model": "[coalesce(parameters('deployments'), createArray())[copyIndex()].model]", - "raiPolicyName": "[tryGet(coalesce(parameters('deployments'), createArray())[copyIndex()], 'raiPolicyName')]", - "versionUpgradeOption": "[tryGet(coalesce(parameters('deployments'), createArray())[copyIndex()], 'versionUpgradeOption')]" - }, - "sku": "[coalesce(tryGet(coalesce(parameters('deployments'), createArray())[copyIndex()], 'sku'), createObject('name', parameters('sku'), 'capacity', tryGet(parameters('sku'), 'capacity'), 'tier', tryGet(parameters('sku'), 'tier'), 'size', tryGet(parameters('sku'), 'size'), 'family', tryGet(parameters('sku'), 'family')))]", - "dependsOn": [ - "cognitiveService" - ] + "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')]" + } + } }, - "cognitiveService_lock": { + "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.CognitiveServices/accounts/{0}', parameters('name'))]", + "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": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" }, "dependsOn": [ - "cognitiveService" - ] - }, - "cognitiveService_diagnosticSettings": { - "copy": { - "name": "cognitiveService_diagnosticSettings", - "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" - }, - "type": "Microsoft.Insights/diagnosticSettings", - "apiVersion": "2021-05-01-preview", - "scope": "[format('Microsoft.CognitiveServices/accounts/{0}', parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", - "properties": { - "copy": [ - { - "name": "metrics", - "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", - "input": { - "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", - "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", - "timeGrain": null - } - }, - { - "name": "logs", - "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", - "input": { - "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", - "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", - "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" - } - } - ], - "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", - "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", - "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", - "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", - "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", - "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" - }, - "dependsOn": [ - "cognitiveService" + "privateEndpoint" ] }, - "cognitiveService_roleAssignments": { + "privateEndpoint_roleAssignments": { "copy": { - "name": "cognitiveService_roleAssignments", + "name": "privateEndpoint_roleAssignments", "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.CognitiveServices/accounts/{0}', parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "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]", @@ -38197,19 +31627,14 @@ "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" }, "dependsOn": [ - "cognitiveService" + "privateEndpoint" ] }, - "cognitiveService_privateEndpoints": { - "copy": { - "name": "cognitiveService_privateEndpoints", - "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]" - }, + "privateEndpoint_privateDnsZoneGroup": { + "condition": "[not(empty(parameters('privateDnsZoneGroup')))]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-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]]", + "name": "[format('{0}-PrivateEndpoint-PrivateDnsZoneGroup', uniqueString(deployment().name))]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -38217,42 +31642,13 @@ "mode": "Incremental", "parameters": { "name": { - "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'name'), format('pep-{0}-{1}-{2}', last(split(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'account'), copyIndex()))]" - }, - "privateLinkServiceConnections": "[if(not(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true())), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'account'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'account')))))), createObject('value', null()))]", - "manualPrivateLinkServiceConnections": "[if(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true()), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'account'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'account')), 'requestMessage', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualConnectionRequestMessage'), 'Manual approval required.'))))), createObject('value', null()))]", - "subnetResourceId": { - "value": "[coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId]" - }, - "enableTelemetry": { - "value": "[variables('enableReferencedModulesTelemetry')]" - }, - "location": { - "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'location'), reference(split(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location)]" - }, - "lock": { - "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" - }, - "privateDnsZoneGroup": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroup')]" - }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'roleAssignments')]" - }, - "tags": { - "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" - }, - "customDnsConfigs": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customDnsConfigs')]" - }, - "ipConfigurations": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'ipConfigurations')]" + "value": "[tryGet(parameters('privateDnsZoneGroup'), 'name')]" }, - "applicationSecurityGroupResourceIds": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'applicationSecurityGroupResourceIds')]" + "privateEndpointName": { + "value": "[parameters('name')]" }, - "customNetworkInterfaceName": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customNetworkInterfaceName')]" + "privateDnsZoneConfigs": { + "value": "[parameters('privateDnsZoneGroup').privateDnsZoneGroupConfigs]" } }, "template": { @@ -38262,1402 +31658,310 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.34.44.8038", - "templateHash": "12389807800450456797" + "version": "0.33.13.18514", + "templateHash": "5440815542537978381" }, - "name": "Private Endpoints", - "description": "This module deploys a Private Endpoint." + "name": "Private Endpoint Private DNS Zone Groups", + "description": "This module deploys a Private Endpoint Private DNS Zone Group." }, "definitions": { - "privateDnsZoneGroupType": { + "privateDnsZoneGroupConfigType": { "type": "object", "properties": { "name": { "type": "string", "nullable": true, "metadata": { - "description": "Optional. The name of the Private DNS Zone Group." + "description": "Optional. The name of the private DNS zone group config." } }, - "privateDnsZoneGroupConfigs": { - "type": "array", - "items": { - "$ref": "#/definitions/privateDnsZoneGroupConfigType" - }, + "privateDnsZoneResourceId": { + "type": "string", "metadata": { - "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + "description": "Required. The resource id of the private DNS zone." } } }, "metadata": { "__bicep_export!": true } + } + }, + "parameters": { + "privateEndpointName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment." + } }, - "ipConfigurationType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the resource that is unique within a resource group." - } - }, - "properties": { - "type": "object", - "properties": { - "groupId": { - "type": "string", - "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." - } - }, - "memberName": { - "type": "string", - "metadata": { - "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." - } - }, - "privateIPAddress": { - "type": "string", - "metadata": { - "description": "Required. A private IP address obtained from the private endpoint's subnet." - } - } - }, - "metadata": { - "description": "Required. Properties of private endpoint IP configurations." - } - } - }, - "metadata": { - "__bicep_export!": true - } - }, - "privateLinkServiceConnectionType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the private link service connection." - } - }, - "properties": { - "type": "object", - "properties": { - "groupIds": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." - } - }, - "privateLinkServiceId": { - "type": "string", - "metadata": { - "description": "Required. The resource id of private link service." - } - }, - "requestMessage": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." - } - } - }, - "metadata": { - "description": "Required. Properties of private link service connection." - } - } - }, - "metadata": { - "__bicep_export!": true - } - }, - "customDnsConfigType": { - "type": "object", - "properties": { - "fqdn": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. FQDN that resolves to private endpoint IP address." - } - }, - "ipAddresses": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. A list of private IP addresses of the private endpoint." - } - } + "privateDnsZoneConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" }, + "minLength": 1, + "maxLength": 5, "metadata": { - "__bicep_export!": true + "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." } }, - "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." - } - } - }, + "name": { + "type": "string", + "defaultValue": "default", "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.5.1" - } + "description": "Optional. The name of the private DNS zone group." } - }, - "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." + } + }, + "variables": { + "copy": [ + { + "name": "privateDnsZoneConfigsVar", + "count": "[length(parameters('privateDnsZoneConfigs'))]", + "input": { + "name": "[coalesce(tryGet(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')], 'name'), last(split(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId, '/')))]", + "properties": { + "privateDnsZoneId": "[parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId]" } } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "private-dns-zone-group/main.bicep" - } } + ] + }, + "resources": { + "privateEndpoint": { + "existing": true, + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-11-01", + "name": "[parameters('privateEndpointName')]" }, - "roleAssignmentType": { - "type": "object", + "privateDnsZoneGroup": { + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2023-11-01", + "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", "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.5.1" - } + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" } } }, - "parameters": { + "outputs": { "name": { "type": "string", "metadata": { - "description": "Required. Name of the private endpoint resource to create." - } - }, - "subnetResourceId": { - "type": "string", - "metadata": { - "description": "Required. Resource ID of the subnet where the endpoint needs to be created." - } - }, - "applicationSecurityGroupResourceIds": { - "type": "array", - "items": { - "type": "string" + "description": "The name of the private endpoint DNS zone group." }, - "nullable": true, - "metadata": { - "description": "Optional. Application security groups in which the private endpoint IP configuration is included." - } + "value": "[parameters('name')]" }, - "customNetworkInterfaceName": { + "resourceId": { "type": "string", - "nullable": true, "metadata": { - "description": "Optional. The custom name of the network interface attached to the private endpoint." - } - }, - "ipConfigurations": { - "type": "array", - "items": { - "$ref": "#/definitions/ipConfigurationType" + "description": "The resource ID of the private endpoint DNS zone group." }, - "nullable": true, - "metadata": { - "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." - } - }, - "privateDnsZoneGroup": { - "$ref": "#/definitions/privateDnsZoneGroupType", - "nullable": true, - "metadata": { - "description": "Optional. The private DNS zone group to configure for the private endpoint." - } + "value": "[resourceId('Microsoft.Network/privateEndpoints/privateDnsZoneGroups', parameters('privateEndpointName'), parameters('name'))]" }, - "location": { + "resourceGroupName": { "type": "string", - "defaultValue": "[resourceGroup().location]", - "metadata": { - "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", - "nullable": true, - "metadata": { - "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." - } - }, - "customDnsConfigs": { - "type": "array", - "items": { - "$ref": "#/definitions/customDnsConfigType" - }, - "nullable": true, - "metadata": { - "description": "Optional. Custom DNS configurations." - } - }, - "manualPrivateLinkServiceConnections": { - "type": "array", - "items": { - "$ref": "#/definitions/privateLinkServiceConnectionType" - }, - "nullable": true, "metadata": { - "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." - } - }, - "privateLinkServiceConnections": { - "type": "array", - "items": { - "$ref": "#/definitions/privateLinkServiceConnectionType" + "description": "The resource group the private endpoint DNS zone group was deployed into." }, - "nullable": true, - "metadata": { - "description": "Conditional. A grouping of information about the connection to the remote resource. Required if `manualPrivateLinkServiceConnections` is empty." - } - }, - "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": "2024-03-01", - "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.11.0', '.', '-'), 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-05-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": "[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": "2022-09-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.34.44.8038", - "templateHash": "13997305779829540948" - }, - "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 - } - } - }, - "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." - } - } - }, - "variables": { - "copy": [ - { - "name": "privateDnsZoneConfigsVar", - "count": "[length(parameters('privateDnsZoneConfigs'))]", - "input": { - "name": "[coalesce(tryGet(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')], 'name'), last(split(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId, '/')))]", - "properties": { - "privateDnsZoneId": "[parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId]" - } - } - } - ] - }, - "resources": { - "privateEndpoint": { - "existing": true, - "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2024-05-01", - "name": "[parameters('privateEndpointName')]" - }, - "privateDnsZoneGroup": { - "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2024-05-01", - "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", - "properties": { - "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" - } - } - }, - "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-05-01', 'full').location]" - }, - "customDnsConfigs": { - "type": "array", - "items": { - "$ref": "#/definitions/customDnsConfigType" - }, - "metadata": { - "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": [ - "cognitiveService" - ] - }, - "secretsExport": { - "condition": "[not(equals(parameters('secretsExportConfiguration'), null()))]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-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]]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "keyVaultName": { - "value": "[last(split(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '/'))]" - }, - "secretsToSet": { - "value": "[union(createArray(), if(contains(parameters('secretsExportConfiguration'), 'accessKey1Name'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'accessKey1Name'), 'value', listKeys('cognitiveService', '2025-04-01-preview').key1)), createArray()), if(contains(parameters('secretsExportConfiguration'), 'accessKey2Name'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'accessKey2Name'), 'value', listKeys('cognitiveService', '2025-04-01-preview').key2)), createArray()))]" - } - }, - "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.1.42791", - "templateHash": "1200612323329026557" - } - }, - "definitions": { - "secretSetOutputType": { - "type": "object", - "properties": { - "secretResourceId": { - "type": "string", - "metadata": { - "description": "The resourceId of the exported secret." - } - }, - "secretUri": { - "type": "string", - "metadata": { - "description": "The secret URI of the exported secret." - } - }, - "secretUriWithVersion": { - "type": "string", - "metadata": { - "description": "The secret URI with version of the exported secret." - } - } - }, - "metadata": { - "description": "An AVM-aligned type for the output of the secret set via the secrets export feature.", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } - } - }, - "secretToSetType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the secret to set." - } - }, - "value": { - "type": "securestring", - "metadata": { - "description": "Required. The value of the secret to set." - } - } - }, - "metadata": { - "description": "An AVM-aligned type for the secret to set via the secrets export feature.", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } - } - } - }, - "parameters": { - "keyVaultName": { - "type": "string", - "metadata": { - "description": "Required. The name of the Key Vault to set the ecrets in." - } - }, - "secretsToSet": { - "type": "array", - "items": { - "$ref": "#/definitions/secretToSetType" - }, - "metadata": { - "description": "Required. The secrets to set in the Key Vault." - } - } - }, - "resources": { - "keyVault": { - "existing": true, - "type": "Microsoft.KeyVault/vaults", - "apiVersion": "2023-07-01", - "name": "[parameters('keyVaultName')]" - }, - "secrets": { - "copy": { - "name": "secrets", - "count": "[length(parameters('secretsToSet'))]" - }, - "type": "Microsoft.KeyVault/vaults/secrets", - "apiVersion": "2023-07-01", - "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('secretsToSet')[copyIndex()].name)]", - "properties": { - "value": "[parameters('secretsToSet')[copyIndex()].value]" - } - } - }, - "outputs": { - "secretsSet": { - "type": "array", - "items": { - "$ref": "#/definitions/secretSetOutputType" - }, - "metadata": { - "description": "The references to the secrets exported to the provided Key Vault." - }, - "copy": { - "count": "[length(range(0, length(coalesce(parameters('secretsToSet'), createArray()))))]", - "input": { - "secretResourceId": "[resourceId('Microsoft.KeyVault/vaults/secrets', parameters('keyVaultName'), parameters('secretsToSet')[range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()]].name)]", - "secretUri": "[reference(format('secrets[{0}]', range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()])).secretUri]", - "secretUriWithVersion": "[reference(format('secrets[{0}]', range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()])).secretUriWithVersion]" - } - } + "value": "[resourceGroup().name]" } } } }, "dependsOn": [ - "cognitiveService" + "privateEndpoint" ] } }, "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the cognitive services account." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the cognitive services account." - }, - "value": "[resourceId('Microsoft.CognitiveServices/accounts', parameters('name'))]" - }, "resourceGroupName": { "type": "string", "metadata": { - "description": "The resource group the cognitive services account was deployed into." + "description": "The resource group the private endpoint was deployed into." }, "value": "[resourceGroup().name]" }, - "endpoint": { + "resourceId": { "type": "string", "metadata": { - "description": "The service endpoint of the cognitive services account." - }, - "value": "[reference('cognitiveService').endpoint]" - }, - "endpoints": { - "$ref": "#/definitions/endpointType", - "metadata": { - "description": "All endpoints available for the cognitive services account, types depends on the cognitive service kind." + "description": "The resource ID of the private endpoint." }, - "value": "[reference('cognitiveService').endpoints]" + "value": "[resourceId('Microsoft.Network/privateEndpoints', parameters('name'))]" }, - "systemAssignedMIPrincipalId": { + "name": { "type": "string", - "nullable": true, "metadata": { - "description": "The principal ID of the system assigned identity." + "description": "The name of the private endpoint." }, - "value": "[tryGet(tryGet(reference('cognitiveService', '2025-04-01-preview', 'full'), 'identity'), 'principalId')]" + "value": "[parameters('name')]" }, "location": { "type": "string", "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('cognitiveService', '2025-04-01-preview', 'full').location]" + "value": "[reference('privateEndpoint', '2023-11-01', 'full').location]" }, - "exportedSecrets": { - "$ref": "#/definitions/secretsOutputType", + "customDnsConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/customDnsConfigType" + }, "metadata": { - "description": "A hashtable of references to the secrets exported to the provided Key Vault. The key of each reference is each secret's name." + "description": "The custom DNS configurations of the private endpoint." }, - "value": "[if(not(equals(parameters('secretsExportConfiguration'), null())), toObject(reference('secretsExport').outputs.secretsSet.value, lambda('secret', last(split(lambdaVariables('secret').secretResourceId, '/'))), lambda('secret', lambdaVariables('secret'))), createObject())]" + "value": "[reference('privateEndpoint').customDnsConfigs]" }, - "privateEndpoints": { + "networkInterfaceResourceIds": { "type": "array", "items": { - "$ref": "#/definitions/privateEndpointOutputType" + "type": "string" }, "metadata": { - "description": "The private endpoints of the congitive services account." + "description": "The resource IDs of the network interfaces associated with the private endpoint." }, - "copy": { - "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]", - "input": { - "name": "[reference(format('cognitiveService_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", - "resourceId": "[reference(format('cognitiveService_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", - "groupId": "[tryGet(tryGet(reference(format('cognitiveService_privateEndpoints[{0}]', copyIndex())).outputs, 'groupId'), 'value')]", - "customDnsConfigs": "[reference(format('cognitiveService_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfigs.value]", - "networkInterfaceResourceIds": "[reference(format('cognitiveService_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceResourceIds.value]" - } - } + "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))]" } } } - } - } - }, - "outputs": { - "resourceId": { - "type": "string", - "value": "[reference('cognitiveService').outputs.resourceId.value]" - }, - "name": { - "type": "string", - "value": "[reference('cognitiveService').outputs.name.value]" - }, - "systemAssignedMIPrincipalId": { - "type": "string", - "nullable": true, - "value": "[tryGet(tryGet(reference('cognitiveService').outputs, 'systemAssignedMIPrincipalId'), 'value')]" - }, - "endpoint": { - "type": "string", - "value": "[reference('cognitiveService').outputs.endpoint.value]" + }, + "dependsOn": [ + "storageAccount" + ] }, - "foundryConnection": { - "type": "object", - "value": { - "name": "[reference('cognitiveService').outputs.name.value]", - "value": null, - "category": "[parameters('category')]", - "target": "[reference('cognitiveService').outputs.endpoint.value]", - "kind": "[parameters('kind')]", - "connectionProperties": { - "authType": "AAD" - }, - "isSharedToAll": true, - "metadata": { - "ApiType": "Azure", - "Kind": "[parameters('kind')]", - "ResourceId": "[reference('cognitiveService').outputs.resourceId.value]" - } - } - } - } - } - }, - "dependsOn": [ - "cognitiveServicesPrivateDnsZone", - "openAiPrivateDnsZone" - ] - }, - "contentSafety": { - "condition": "[parameters('contentSafetyEnabled')]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[take(format('{0}-content-safety-deployment', parameters('name')), 64)]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[format('safety{0}{1}', parameters('name'), parameters('resourceToken'))]" - }, - "location": { - "value": "[parameters('location')]" - }, - "kind": { - "value": "ContentSafety" - }, - "networkIsolation": { - "value": "[parameters('networkIsolation')]" - }, - "networkAcls": { - "value": "[parameters('networkAcls')]" - }, - "virtualNetworkSubnetResourceId": "[if(parameters('networkIsolation'), createObject('value', parameters('virtualNetworkSubnetResourceId')), createObject('value', ''))]", - "privateDnsZonesResourceIds": "[if(parameters('networkIsolation'), createObject('value', createArray(reference('cognitiveServicesPrivateDnsZone').outputs.resourceId.value)), createObject('value', createArray()))]", - "logAnalyticsWorkspaceResourceId": { - "value": "[parameters('logAnalyticsWorkspaceResourceId')]" - }, - "tags": { - "value": "[parameters('tags')]" - } - }, - "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.1.42791", - "templateHash": "2348080591288311162" - } - }, - "definitions": { - "deploymentsType": { - "type": "object", + "storageAccount_managementPolicies": { + "condition": "[not(empty(coalesce(parameters('managementPolicyRules'), createArray())))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Storage-ManagementPolicies', uniqueString(deployment().name, parameters('location')))]", "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Specify the name of cognitive service account deployment." + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "storageAccountName": { + "value": "[parameters('name')]" + }, + "rules": { + "value": "[coalesce(parameters('managementPolicyRules'), createArray())]" } }, - "model": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of Cognitive Services account deployment model." - } + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.33.13.18514", + "templateHash": "4967204006599351003" }, - "format": { + "name": "Storage Account Management Policies", + "description": "This module deploys a Storage Account Management Policy." + }, + "parameters": { + "storageAccountName": { "type": "string", + "maxLength": 24, "metadata": { - "description": "Required. The format of Cognitive Services account deployment model." + "description": "Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment." } }, - "version": { - "type": "string", + "rules": { + "type": "array", "metadata": { - "description": "Required. The version of Cognitive Services account deployment model." + "description": "Required. The Storage Account ManagementPolicies Rules." } } }, - "metadata": { - "description": "Required. Properties of Cognitive Services account deployment model." - } - }, - "sku": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the resource model definition representing SKU." - } - }, - "capacity": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The capacity of the resource model definition representing SKU." + "resources": [ + { + "type": "Microsoft.Storage/storageAccounts/managementPolicies", + "apiVersion": "2023-01-01", + "name": "[format('{0}/{1}', parameters('storageAccountName'), 'default')]", + "properties": { + "policy": { + "rules": "[parameters('rules')]" + } } - }, - "tier": { + } + ], + "outputs": { + "resourceId": { "type": "string", - "nullable": true, "metadata": { - "description": "Optional. The tier of the resource model definition representing SKU." - } + "description": "The resource ID of the deployed management policy." + }, + "value": "default" }, - "size": { + "name": { "type": "string", - "nullable": true, "metadata": { - "description": "Optional. The size of the resource model definition representing SKU." - } + "description": "The name of the deployed management policy." + }, + "value": "default" }, - "family": { + "resourceGroupName": { "type": "string", - "nullable": true, "metadata": { - "description": "Optional. The family of the resource model definition representing SKU." - } + "description": "The resource group of the deployed management policy." + }, + "value": "[resourceGroup().name]" } - }, - "nullable": true, - "metadata": { - "description": "Optional. The resource model definition representing SKU." - } - }, - "raiPolicyName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of RAI policy." - } - }, - "versionUpgradeOption": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The version upgrade option." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "../customTypes.bicep" - } - } - }, - "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.5.1" - } - } - } - }, - "parameters": { - "name": { - "type": "string", - "metadata": { - "description": "Name of the Cognitive Services resource. Must be unique in the resource group." - } - }, - "location": { - "type": "string", - "metadata": { - "description": "The location of the Cognitive Services resource." - } - }, - "kind": { - "type": "string", - "allowedValues": [ - "AIServices", - "AnomalyDetector", - "CognitiveServices", - "ComputerVision", - "ContentModerator", - "ContentSafety", - "ConversationalLanguageUnderstanding", - "CustomVision.Prediction", - "CustomVision.Training", - "Face", - "FormRecognizer", - "HealthInsights", - "ImmersiveReader", - "Internal.AllInOne", - "LUIS", - "LUIS.Authoring", - "LanguageAuthoring", - "MetricsAdvisor", - "OpenAI", - "Personalizer", - "QnAMaker.v2", - "SpeechServices", - "TextAnalytics", - "TextTranslation" - ], - "metadata": { - "description": "Required. Kind of the Cognitive Services account. Use 'Get-AzCognitiveServicesAccountSku' to determine a valid combinations of 'kind' and 'SKU' for your Azure region." - } - }, - "sku": { - "type": "string", - "defaultValue": "S0", - "allowedValues": [ - "S", - "S0", - "S1", - "S2", - "S3", - "S4", - "S5", - "S6", - "S7", - "S8" - ], - "metadata": { - "description": "Required. The SKU of the Cognitive Services account. Use 'Get-AzCognitiveServicesAccountSku' to determine a valid combinations of 'kind' and 'SKU' for your Azure region." - } - }, - "category": { - "type": "string", - "defaultValue": "CognitiveService", - "metadata": { - "description": "Category of the Cognitive Services account." - } - }, - "networkIsolation": { - "type": "bool", - "metadata": { - "description": "Specifies whether to enable network isolation. If true, the resource will be deployed in a private endpoint and public network access will be disabled." - } - }, - "privateDnsZonesResourceIds": { - "type": "array", - "items": { - "type": "string" - }, - "defaultValue": [], - "metadata": { - "description": "Existing resource ID of the private DNS zone for the private endpoint." - } - }, - "virtualNetworkSubnetResourceId": { - "type": "string", - "metadata": { - "description": "Resource ID of the subnet for the private endpoint." - } - }, - "logAnalyticsWorkspaceResourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the Log Analytics workspace to use for diagnostic settings." - } - }, - "aiModelDeployments": { - "type": "array", - "items": { - "$ref": "#/definitions/deploymentsType" - }, - "defaultValue": [], - "metadata": { - "description": "Optional. Specifies the OpenAI deployments to create." - } + "dependsOn": [ + "storageAccount", + "storageAccount_blobServices" + ] }, - "roleAssignments": { - "type": "array", - "items": { - "$ref": "#/definitions/roleAssignmentType" + "storageAccount_localUsers": { + "copy": { + "name": "storageAccount_localUsers", + "count": "[length(coalesce(parameters('localUsers'), createArray()))]" }, - "nullable": true, - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "tags": { - "type": "object", - "defaultValue": {}, - "metadata": { - "description": "Optional. Tags to be applied to the resources." - } - }, - "networkAcls": { - "type": "object", - "metadata": { - "description": "Optional. A collection of rules governing the accessibility from specific network locations." - } - } - }, - "variables": { - "copy": [ - { - "name": "privateDnsZones", - "count": "[length(parameters('privateDnsZonesResourceIds'))]", - "input": { - "privateDnsZoneResourceId": "[parameters('privateDnsZonesResourceIds')[copyIndex('privateDnsZones')]]" - } - } - ], - "nameFormatted": "[take(toLower(parameters('name')), 24)]" - }, - "resources": { - "cognitiveService": { "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[take(format('cog-{0}-{1}-deployment', parameters('kind'), parameters('name')), 64)]", + "name": "[format('{0}-Storage-LocalUsers-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "name": { - "value": "[variables('nameFormatted')]" - }, - "location": { - "value": "[parameters('location')]" - }, - "tags": { - "value": "[parameters('tags')]" - }, - "sku": { - "value": "[parameters('sku')]" - }, - "kind": { - "value": "[parameters('kind')]" - }, - "allowProjectManagement": { - "value": true - }, - "managedIdentities": { - "value": { - "systemAssigned": true - } + "storageAccountName": { + "value": "[parameters('name')]" }, - "deployments": { - "value": "[parameters('aiModelDeployments')]" + "name": { + "value": "[coalesce(parameters('localUsers'), createArray())[copyIndex()].name]" }, - "customSubDomainName": { - "value": "[parameters('name')]" + "hasSshKey": { + "value": "[coalesce(parameters('localUsers'), createArray())[copyIndex()].hasSshKey]" }, - "disableLocalAuth": { - "value": "[parameters('networkIsolation')]" + "hasSshPassword": { + "value": "[coalesce(parameters('localUsers'), createArray())[copyIndex()].hasSshPassword]" }, - "publicNetworkAccess": "[if(parameters('networkIsolation'), createObject('value', 'Disabled'), createObject('value', 'Enabled'))]", - "diagnosticSettings": { - "value": [ - { - "workspaceResourceId": "[parameters('logAnalyticsWorkspaceResourceId')]" - } - ] + "permissionScopes": { + "value": "[coalesce(parameters('localUsers'), createArray())[copyIndex()].permissionScopes]" }, - "roleAssignments": { - "value": "[parameters('roleAssignments')]" + "hasSharedKey": { + "value": "[tryGet(coalesce(parameters('localUsers'), createArray())[copyIndex()], 'hasSharedKey')]" }, - "networkAcls": { - "value": "[parameters('networkAcls')]" + "homeDirectory": { + "value": "[tryGet(coalesce(parameters('localUsers'), createArray())[copyIndex()], 'homeDirectory')]" }, - "privateEndpoints": "[if(parameters('networkIsolation'), createObject('value', createArray(createObject('privateDnsZoneGroup', createObject('privateDnsZoneGroupConfigs', variables('privateDnsZones')), 'subnetResourceId', parameters('virtualNetworkSubnetResourceId')))), createObject('value', createArray()))]" + "sshAuthorizedKeys": { + "value": "[tryGet(coalesce(parameters('localUsers'), createArray())[copyIndex()], 'sshAuthorizedKeys')]" + } }, "template": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", @@ -39666,401 +31970,309 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "16135659971302525380" + "version": "0.33.13.18514", + "templateHash": "2528560857012083896" }, - "name": "Cognitive Services", - "description": "This module deploys a Cognitive Service." + "name": "Storage Account Local Users", + "description": "This module deploys a Storage Account Local User, which is used for SFTP authentication." }, "definitions": { - "privateEndpointOutputType": { + "sshAuthorizedKeyType": { "type": "object", "properties": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the private endpoint." - } - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the private endpoint." - } - }, - "groupId": { + "description": { "type": "string", "nullable": true, "metadata": { - "description": "The group Id for the private endpoint Group." - } - }, - "customDnsConfigs": { - "type": "array", - "items": { - "type": "object", - "properties": { - "fqdn": { - "type": "string", - "nullable": true, - "metadata": { - "description": "FQDN that resolves to private endpoint IP address." - } - }, - "ipAddresses": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "A list of private IP addresses of the private endpoint." - } - } - } - }, - "metadata": { - "description": "The custom DNS configurations of the private endpoint." + "description": "Optional. Description used to store the function/usage of the key." } }, - "networkInterfaceResourceIds": { - "type": "array", - "items": { - "type": "string" - }, + "key": { + "type": "securestring", "metadata": { - "description": "The IDs of the network interfaces associated with the private endpoint." + "description": "Required. SSH public key base64 encoded. The format should be: '{keyType} {keyData}', e.g. ssh-rsa AAAABBBB." } } }, "metadata": { - "__bicep_export!": true, - "description": "The type for the private endpoint output." + "__bicep_export!": true } }, - "deploymentType": { + "permissionScopeType": { "type": "object", "properties": { - "name": { + "permissions": { "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Specify the name of cognitive service account deployment." - } - }, - "model": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of Cognitive Services account deployment model." - } - }, - "format": { - "type": "string", - "metadata": { - "description": "Required. The format of Cognitive Services account deployment model." - } - }, - "version": { - "type": "string", - "metadata": { - "description": "Required. The version of Cognitive Services account deployment model." - } - } - }, "metadata": { - "description": "Required. Properties of Cognitive Services account deployment model." + "description": "Required. The permissions for the local user. Possible values include: Read (r), Write (w), Delete (d), List (l), and Create (c)." } }, - "sku": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the resource model definition representing SKU." - } - }, - "capacity": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The capacity of the resource model definition representing SKU." - } - }, - "tier": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The tier of the resource model definition representing SKU." - } - }, - "size": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The size of the resource model definition representing SKU." - } - }, - "family": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The family of the resource model definition representing SKU." - } - } - }, - "nullable": true, + "resourceName": { + "type": "string", "metadata": { - "description": "Optional. The resource model definition representing SKU." + "description": "Required. The name of resource, normally the container name or the file share name, used by the local user." } }, - "raiPolicyName": { + "service": { "type": "string", - "nullable": true, "metadata": { - "description": "Optional. The name of RAI policy." - } - }, - "versionUpgradeOption": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The version upgrade option." + "description": "Required. The service used by the local user, e.g. blob, file." } } }, "metadata": { - "__bicep_export!": true, - "description": "The type for a cognitive services account deployment." + "__bicep_export!": true + } + } + }, + "parameters": { + "storageAccountName": { + "type": "string", + "maxLength": 24, + "metadata": { + "description": "Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment." } }, - "endpointType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Type of the endpoint." - } - }, - "endpoint": { - "type": "string", - "nullable": true, - "metadata": { - "description": "The endpoint URI." - } - } - }, + "name": { + "type": "string", "metadata": { - "__bicep_export!": true, - "description": "The type for a cognitive services account endpoint." + "description": "Required. The name of the local user used for SFTP Authentication." } }, - "secretsExportConfigurationType": { - "type": "object", - "properties": { - "keyVaultResourceId": { - "type": "string", - "metadata": { - "description": "Required. The key vault name where to store the keys and connection strings generated by the modules." - } - }, - "accessKey1Name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name for the accessKey1 secret to create." - } - }, - "accessKey2Name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name for the accessKey2 secret to create." - } - } - }, + "hasSharedKey": { + "type": "bool", + "defaultValue": false, "metadata": { - "__bicep_export!": true, - "description": "The type of the secrets exported to the provided Key Vault." + "description": "Optional. Indicates whether shared key exists. Set it to false to remove existing shared key." } }, - "_1.privateEndpointCustomDnsConfigType": { - "type": "object", - "properties": { - "fqdn": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. FQDN that resolves to private endpoint IP address." - } - }, - "ipAddresses": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. A list of private IP addresses of the private endpoint." - } - } - }, + "hasSshKey": { + "type": "bool", "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } + "description": "Required. Indicates whether SSH key exists. Set it to false to remove existing SSH key." } }, - "_1.privateEndpointIpConfigurationType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the resource that is unique within a resource group." - } - }, - "properties": { - "type": "object", - "properties": { - "groupId": { - "type": "string", - "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." - } - }, - "memberName": { - "type": "string", - "metadata": { - "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to." - } - }, - "privateIPAddress": { - "type": "string", - "metadata": { - "description": "Required. A private IP address obtained from the private endpoint's subnet." - } - } - }, - "metadata": { - "description": "Required. Properties of private endpoint IP configurations." - } - } + "hasSshPassword": { + "type": "bool", + "metadata": { + "description": "Required. Indicates whether SSH password exists. Set it to false to remove existing SSH password." + } + }, + "homeDirectory": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The local user home directory." + } + }, + "permissionScopes": { + "type": "array", + "items": { + "$ref": "#/definitions/permissionScopeType" }, "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } + "description": "Required. The permission scopes of the local user." } }, - "_1.privateEndpointPrivateDnsZoneGroupType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the Private DNS Zone Group." - } - }, - "privateDnsZoneGroupConfigs": { - "type": "array", - "items": { - "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": "Required. The private DNS Zone Groups to associate the Private Endpoint. A DNS Zone Group can support up to 5 DNS zones." - } - } + "sshAuthorizedKeys": { + "type": "array", + "items": { + "$ref": "#/definitions/sshAuthorizedKeyType" }, + "nullable": true, "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } + "description": "Optional. The local user SSH authorized keys for SFTP." } + } + }, + "resources": { + "storageAccount": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2023-04-01", + "name": "[parameters('storageAccountName')]" }, - "_1.secretSetOutputType": { - "type": "object", + "localUsers": { + "type": "Microsoft.Storage/storageAccounts/localUsers", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}', parameters('storageAccountName'), parameters('name'))]", "properties": { - "secretResourceId": { - "type": "string", - "metadata": { - "description": "The resourceId of the exported secret." - } - }, - "secretUri": { - "type": "string", - "metadata": { - "description": "The secret URI of the exported secret." - } - }, - "secretUriWithVersion": { - "type": "string", - "metadata": { - "description": "The secret URI with version of the exported secret." - } - } + "hasSharedKey": "[parameters('hasSharedKey')]", + "hasSshKey": "[parameters('hasSshKey')]", + "hasSshPassword": "[parameters('hasSshPassword')]", + "homeDirectory": "[parameters('homeDirectory')]", + "permissionScopes": "[parameters('permissionScopes')]", + "sshAuthorizedKeys": "[parameters('sshAuthorizedKeys')]" + } + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed local user." }, + "value": "[parameters('name')]" + }, + "resourceGroupName": { + "type": "string", "metadata": { - "description": "An AVM-aligned type for the output of the secret set via the secrets export feature.", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } - } + "description": "The resource group of the deployed local user." + }, + "value": "[resourceGroup().name]" }, - "customerManagedKeyType": { + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed local user." + }, + "value": "[resourceId('Microsoft.Storage/storageAccounts/localUsers', parameters('storageAccountName'), parameters('name'))]" + } + } + } + }, + "dependsOn": [ + "storageAccount" + ] + }, + "storageAccount_blobServices": { + "condition": "[not(empty(parameters('blobServices')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Storage-BlobServices', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "storageAccountName": { + "value": "[parameters('name')]" + }, + "containers": { + "value": "[tryGet(parameters('blobServices'), 'containers')]" + }, + "automaticSnapshotPolicyEnabled": { + "value": "[tryGet(parameters('blobServices'), 'automaticSnapshotPolicyEnabled')]" + }, + "changeFeedEnabled": { + "value": "[tryGet(parameters('blobServices'), 'changeFeedEnabled')]" + }, + "changeFeedRetentionInDays": { + "value": "[tryGet(parameters('blobServices'), 'changeFeedRetentionInDays')]" + }, + "containerDeleteRetentionPolicyEnabled": { + "value": "[tryGet(parameters('blobServices'), 'containerDeleteRetentionPolicyEnabled')]" + }, + "containerDeleteRetentionPolicyDays": { + "value": "[tryGet(parameters('blobServices'), 'containerDeleteRetentionPolicyDays')]" + }, + "containerDeleteRetentionPolicyAllowPermanentDelete": { + "value": "[tryGet(parameters('blobServices'), 'containerDeleteRetentionPolicyAllowPermanentDelete')]" + }, + "corsRules": { + "value": "[tryGet(parameters('blobServices'), 'corsRules')]" + }, + "defaultServiceVersion": { + "value": "[tryGet(parameters('blobServices'), 'defaultServiceVersion')]" + }, + "deleteRetentionPolicyAllowPermanentDelete": { + "value": "[tryGet(parameters('blobServices'), 'deleteRetentionPolicyAllowPermanentDelete')]" + }, + "deleteRetentionPolicyEnabled": { + "value": "[tryGet(parameters('blobServices'), 'deleteRetentionPolicyEnabled')]" + }, + "deleteRetentionPolicyDays": { + "value": "[tryGet(parameters('blobServices'), 'deleteRetentionPolicyDays')]" + }, + "isVersioningEnabled": { + "value": "[tryGet(parameters('blobServices'), 'isVersioningEnabled')]" + }, + "lastAccessTimeTrackingPolicyEnabled": { + "value": "[tryGet(parameters('blobServices'), 'lastAccessTimeTrackingPolicyEnabled')]" + }, + "restorePolicyEnabled": { + "value": "[tryGet(parameters('blobServices'), 'restorePolicyEnabled')]" + }, + "restorePolicyDays": { + "value": "[tryGet(parameters('blobServices'), 'restorePolicyDays')]" + }, + "diagnosticSettings": { + "value": "[tryGet(parameters('blobServices'), 'diagnosticSettings')]" + } + }, + "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.33.13.18514", + "templateHash": "17736639420143155642" + }, + "name": "Storage Account blob Services", + "description": "This module deploys a Storage Account Blob Service." + }, + "definitions": { + "corsRuleType": { "type": "object", "properties": { - "keyVaultResourceId": { - "type": "string", + "allowedHeaders": { + "type": "array", + "items": { + "type": "string" + }, "metadata": { - "description": "Required. The resource ID of a key vault to reference a customer managed key for encryption from." + "description": "Required. A list of headers allowed to be part of the cross-origin request." } }, - "keyName": { - "type": "string", + "allowedMethods": { + "type": "array", + "allowedValues": [ + "CONNECT", + "DELETE", + "GET", + "HEAD", + "MERGE", + "OPTIONS", + "PATCH", + "POST", + "PUT", + "TRACE" + ], "metadata": { - "description": "Required. The name of the customer managed key to use for encryption." + "description": "Required. A list of HTTP methods that are allowed to be executed by the origin." } }, - "keyVersion": { - "type": "string", - "nullable": true, + "allowedOrigins": { + "type": "array", + "items": { + "type": "string" + }, "metadata": { - "description": "Optional. The version of the customer managed key to reference for encryption. If not provided, the deployment will use the latest version available at deployment time." + "description": "Required. A list of origin domains that will be allowed via CORS, or \"*\" to allow all domains." } }, - "userAssignedIdentityResourceId": { - "type": "string", - "nullable": true, + "exposedHeaders": { + "type": "array", + "items": { + "type": "string" + }, "metadata": { - "description": "Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use." + "description": "Required. A list of response headers to expose to CORS clients." + } + }, + "maxAgeInSeconds": { + "type": "int", + "metadata": { + "description": "Required. The number of seconds that the client/browser should cache a preflight response." } } }, "metadata": { - "description": "An AVM-aligned type for a customer-managed key. To be used if the resource type does not support auto-rotation of the customer-managed key.", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } + "__bicep_export!": true, + "description": "The type for a cors rule." } }, "diagnosticSettingFullType": { @@ -40184,709 +32396,195 @@ "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } + } + }, + "parameters": { + "storageAccountName": { + "type": "string", + "maxLength": 24, + "metadata": { + "description": "Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment." + } }, - "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." - } - } - }, + "automaticSnapshotPolicyEnabled": { + "type": "bool", + "defaultValue": false, "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.5.1" - } + "description": "Optional. Automatic Snapshot is enabled if set to true." } }, - "managedIdentityAllType": { - "type": "object", - "properties": { - "systemAssigned": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Enables system assigned managed identity on the resource." - } - }, - "userAssignedResourceIds": { - "type": "array", - "items": { - "type": "string" - }, - "nullable": true, - "metadata": { - "description": "Optional. The resource ID(s) to assign to the resource. Required if a user assigned identity is used for encryption." - } - } - }, + "changeFeedEnabled": { + "type": "bool", + "defaultValue": false, "metadata": { - "description": "An AVM-aligned type for a managed identity configuration. To be used if both a system-assigned & user-assigned identities are supported by the resource provider.", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } + "description": "Optional. The blob service properties for change feed events. Indicates whether change feed event logging is enabled for the Blob service." } }, - "privateEndpointSingleServiceType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the Private Endpoint." - } - }, - "location": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The location to deploy the Private Endpoint to." - } - }, - "privateLinkServiceConnectionName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the private link connection to create." - } - }, - "service": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The subresource to deploy the Private Endpoint for. For example \"vault\" for a Key Vault Private Endpoint." - } - }, - "subnetResourceId": { - "type": "string", - "metadata": { - "description": "Required. Resource ID of the subnet where the endpoint needs to be created." - } - }, - "resourceGroupResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The resource ID of the Resource Group the Private Endpoint will be created in. If not specified, the Resource Group of the provided Virtual Network Subnet is used." - } - }, - "privateDnsZoneGroup": { - "$ref": "#/definitions/_1.privateEndpointPrivateDnsZoneGroupType", - "nullable": true, - "metadata": { - "description": "Optional. The private DNS Zone Group to configure for the Private Endpoint." - } - }, - "isManualConnection": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. If Manual Private Link Connection is required." - } - }, - "manualConnectionRequestMessage": { - "type": "string", - "nullable": true, - "maxLength": 140, - "metadata": { - "description": "Optional. A message passed to the owner of the remote resource with the manual connection request." - } - }, - "customDnsConfigs": { - "type": "array", - "items": { - "$ref": "#/definitions/_1.privateEndpointCustomDnsConfigType" - }, - "nullable": true, - "metadata": { - "description": "Optional. Custom DNS configurations." - } - }, - "ipConfigurations": { - "type": "array", - "items": { - "$ref": "#/definitions/_1.privateEndpointIpConfigurationType" - }, - "nullable": true, - "metadata": { - "description": "Optional. A list of IP configurations of the Private Endpoint. This will be used to map to the first-party Service endpoints." - } - }, - "applicationSecurityGroupResourceIds": { - "type": "array", - "items": { - "type": "string" - }, - "nullable": true, - "metadata": { - "description": "Optional. Application security groups in which the Private Endpoint IP configuration is included." - } - }, - "customNetworkInterfaceName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The custom name of the network interface attached to the Private Endpoint." - } - }, - "lock": { - "$ref": "#/definitions/lockType", - "nullable": true, - "metadata": { - "description": "Optional. Specify the type of lock." - } - }, - "roleAssignments": { - "type": "array", - "items": { - "$ref": "#/definitions/roleAssignmentType" - }, - "nullable": true, - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "tags": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Tags to be applied on all resources/Resource Groups in this deployment." - } - }, - "enableTelemetry": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Enable/Disable usage telemetry for module." - } - } - }, - "metadata": { - "description": "An AVM-aligned type for a private endpoint. To be used if the private endpoint's default service / groupId can be assumed (i.e., for services that only have one Private Endpoint type like 'vault' for key vault).", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } - } - }, - "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.5.1" - } - } - }, - "secretsOutputType": { - "type": "object", - "properties": {}, - "additionalProperties": { - "$ref": "#/definitions/_1.secretSetOutputType", - "metadata": { - "description": "An exported secret's references." - } - }, - "metadata": { - "description": "A map of the exported secrets", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } - } - } - }, - "parameters": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of Cognitive Services account." - } - }, - "kind": { - "type": "string", - "allowedValues": [ - "AIServices", - "AnomalyDetector", - "CognitiveServices", - "ComputerVision", - "ContentModerator", - "ContentSafety", - "ConversationalLanguageUnderstanding", - "CustomVision.Prediction", - "CustomVision.Training", - "Face", - "FormRecognizer", - "HealthInsights", - "ImmersiveReader", - "Internal.AllInOne", - "LUIS", - "LUIS.Authoring", - "LanguageAuthoring", - "MetricsAdvisor", - "OpenAI", - "Personalizer", - "QnAMaker.v2", - "SpeechServices", - "TextAnalytics", - "TextTranslation" - ], - "metadata": { - "description": "Required. Kind of the Cognitive Services account. Use 'Get-AzCognitiveServicesAccountSku' to determine a valid combinations of 'kind' and 'SKU' for your Azure region." - } - }, - "sku": { - "type": "string", - "defaultValue": "S0", - "allowedValues": [ - "C2", - "C3", - "C4", - "F0", - "F1", - "S", - "S0", - "S1", - "S10", - "S2", - "S3", - "S4", - "S5", - "S6", - "S7", - "S8", - "S9" - ], - "metadata": { - "description": "Optional. SKU of the Cognitive Services account. Use 'Get-AzCognitiveServicesAccountSku' to determine a valid combinations of 'kind' and 'SKU' for your Azure region." - } - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]", - "metadata": { - "description": "Optional. Location for all Resources." - } - }, - "diagnosticSettings": { - "type": "array", - "items": { - "$ref": "#/definitions/diagnosticSettingFullType" - }, - "nullable": true, - "metadata": { - "description": "Optional. The diagnostic settings of the service." - } - }, - "publicNetworkAccess": { - "type": "string", - "nullable": true, - "allowedValues": [ - "Enabled", - "Disabled" - ], - "metadata": { - "description": "Optional. Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set and networkAcls are not set." - } - }, - "customSubDomainName": { - "type": "string", + "changeFeedRetentionInDays": { + "type": "int", "nullable": true, + "minValue": 1, + "maxValue": 146000, "metadata": { - "description": "Conditional. Subdomain name used for token-based authentication. Required if 'networkAcls' or 'privateEndpoints' are set." + "description": "Optional. Indicates whether change feed event logging is enabled for the Blob service. Indicates the duration of changeFeed retention in days. If left blank, it indicates an infinite retention of the change feed." } }, - "networkAcls": { - "type": "object", - "nullable": true, + "containerDeleteRetentionPolicyEnabled": { + "type": "bool", + "defaultValue": true, "metadata": { - "description": "Optional. A collection of rules governing the accessibility from specific network locations." + "description": "Optional. The blob service properties for container soft delete. Indicates whether DeleteRetentionPolicy is enabled." } }, - "privateEndpoints": { - "type": "array", - "items": { - "$ref": "#/definitions/privateEndpointSingleServiceType" - }, + "containerDeleteRetentionPolicyDays": { + "type": "int", "nullable": true, + "minValue": 1, + "maxValue": 365, "metadata": { - "description": "Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible." + "description": "Optional. Indicates the number of days that the deleted item should be retained." } }, - "lock": { - "$ref": "#/definitions/lockType", - "nullable": true, + "containerDeleteRetentionPolicyAllowPermanentDelete": { + "type": "bool", + "defaultValue": false, "metadata": { - "description": "Optional. The lock settings of the service." + "description": "Optional. This property when set to true allows deletion of the soft deleted blob versions and snapshots. This property cannot be used with blob restore policy. This property only applies to blob service and does not apply to containers or file share." } }, - "roleAssignments": { + "corsRules": { "type": "array", "items": { - "$ref": "#/definitions/roleAssignmentType" + "$ref": "#/definitions/corsRuleType" }, "nullable": true, "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "tags": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Tags of the resource." - } - }, - "allowedFqdnList": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. List of allowed FQDN." + "description": "Optional. The List of CORS rules. You can include up to five CorsRule elements in the request." } }, - "apiProperties": { - "type": "object", - "nullable": true, + "defaultServiceVersion": { + "type": "string", + "defaultValue": "", "metadata": { - "description": "Optional. The API properties for special APIs." + "description": "Optional. Indicates the default version to use for requests to the Blob service if an incoming request's version is not specified. Possible values include version 2008-10-27 and all more recent versions." } }, - "disableLocalAuth": { + "deleteRetentionPolicyEnabled": { "type": "bool", "defaultValue": true, "metadata": { - "description": "Optional. Allow only Azure AD authentication. Should be enabled for security reasons." + "description": "Optional. The blob service properties for blob soft delete." } }, - "customerManagedKey": { - "$ref": "#/definitions/customerManagedKeyType", - "nullable": true, + "deleteRetentionPolicyDays": { + "type": "int", + "defaultValue": 7, + "minValue": 1, + "maxValue": 365, "metadata": { - "description": "Optional. The customer managed key definition." + "description": "Optional. Indicates the number of days that the deleted blob should be retained." } }, - "dynamicThrottlingEnabled": { + "deleteRetentionPolicyAllowPermanentDelete": { "type": "bool", "defaultValue": false, "metadata": { - "description": "Optional. The flag to enable dynamic throttling." + "description": "Optional. This property when set to true allows deletion of the soft deleted blob versions and snapshots. This property cannot be used with blob restore policy. This property only applies to blob service and does not apply to containers or file share." } }, - "migrationToken": { - "type": "securestring", - "nullable": true, + "isVersioningEnabled": { + "type": "bool", + "defaultValue": false, "metadata": { - "description": "Optional. Resource migration token." + "description": "Optional. Use versioning to automatically maintain previous versions of your blobs." } }, - "restore": { + "lastAccessTimeTrackingPolicyEnabled": { "type": "bool", "defaultValue": false, "metadata": { - "description": "Optional. Restore a soft-deleted cognitive service at deployment time. Will fail if no such soft-deleted resource exists." + "description": "Optional. The blob service property to configure last access time based tracking policy. When set to true last access time based tracking is enabled." } }, - "restrictOutboundNetworkAccess": { + "restorePolicyEnabled": { "type": "bool", - "defaultValue": true, + "defaultValue": false, "metadata": { - "description": "Optional. Restrict outbound network access." + "description": "Optional. The blob service properties for blob restore policy. If point-in-time restore is enabled, then versioning, change feed, and blob soft delete must also be enabled." } }, - "userOwnedStorage": { - "type": "array", - "nullable": true, + "restorePolicyDays": { + "type": "int", + "defaultValue": 6, + "minValue": 1, "metadata": { - "description": "Optional. The storage accounts for this resource." + "description": "Optional. How long this blob can be restored. It should be less than DeleteRetentionPolicy days." } }, - "managedIdentities": { - "$ref": "#/definitions/managedIdentityAllType", + "containers": { + "type": "array", "nullable": true, "metadata": { - "description": "Optional. The managed identity definition for this resource." - } - }, - "enableTelemetry": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Optional. Enable/Disable usage telemetry for module." + "description": "Optional. Blob containers to create." } }, - "deployments": { + "diagnosticSettings": { "type": "array", "items": { - "$ref": "#/definitions/deploymentType" + "$ref": "#/definitions/diagnosticSettingFullType" }, "nullable": true, "metadata": { - "description": "Optional. Array of deployments about cognitive service accounts to create." - } - }, - "secretsExportConfiguration": { - "$ref": "#/definitions/secretsExportConfigurationType", - "nullable": true, - "metadata": { - "description": "Optional. Key vault reference and secret settings for the module's secrets export." - } - }, - "allowProjectManagement": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Enable/Disable project management feature for AI Foundry." + "description": "Optional. The diagnostic settings of the service." } } }, "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)))))]" - } - ], - "enableReferencedModulesTelemetry": false, - "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", - "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned, UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', null())), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", - "builtInRoleNames": { - "Cognitive Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '25fbc0a9-bd7c-42a3-aa1a-3b75d497ee68')]", - "Cognitive Services Custom Vision Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c1ff6cc2-c111-46fe-8896-e0ef812ad9f3')]", - "Cognitive Services Custom Vision Deployment": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5c4089e1-6d96-4d2f-b296-c1bc7137275f')]", - "Cognitive Services Custom Vision Labeler": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '88424f51-ebe7-446f-bc41-7fa16989e96c')]", - "Cognitive Services Custom Vision Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '93586559-c37d-4a6b-ba08-b9f0940c2d73')]", - "Cognitive Services Custom Vision Trainer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0a5ae4ab-0d65-4eeb-be61-29fc9b54394b')]", - "Cognitive Services Data Reader (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b59867f0-fa02-499b-be73-45a86b5b3e1c')]", - "Cognitive Services Face Recognizer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9894cab4-e18a-44aa-828b-cb588cd6f2d7')]", - "Cognitive Services Immersive Reader User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b2de6794-95db-4659-8781-7e080d3f2b9d')]", - "Cognitive Services Language Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f07febfe-79bc-46b1-8b37-790e26e6e498')]", - "Cognitive Services Language Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7628b7b8-a8b2-4cdc-b46f-e9b35248918e')]", - "Cognitive Services Language Writer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f2310ca1-dc64-4889-bb49-c8e0fa3d47a8')]", - "Cognitive Services LUIS Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f72c8140-2111-481c-87ff-72b910f6e3f8')]", - "Cognitive Services LUIS Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18e81cdc-4e98-4e29-a639-e7d10c5a6226')]", - "Cognitive Services LUIS Writer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6322a993-d5c9-4bed-b113-e49bbea25b27')]", - "Cognitive Services Metrics Advisor Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'cb43c632-a144-4ec5-977c-e80c4affc34a')]", - "Cognitive Services Metrics Advisor User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3b20f47b-3825-43cb-8114-4bd2201156a8')]", - "Cognitive Services OpenAI Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a001fd3d-188f-4b5d-821b-7da978bf7442')]", - "Cognitive Services OpenAI User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e0bd9bd-7b93-4f28-af87-19fc36ad61bd')]", - "Cognitive Services QnA Maker Editor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f4cc2bf9-21be-47a1-bdf1-5c5804381025')]", - "Cognitive Services QnA Maker Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '466ccd10-b268-4a11-b098-b4849f024126')]", - "Cognitive Services Speech Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0e75ca1e-0464-4b4d-8b93-68208a576181')]", - "Cognitive Services Speech User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f2dc8367-1007-4938-bd23-fe263f013447')]", - "Cognitive Services User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a97b65f3-24c7-4388-baec-2e87135dc908')]", - "Azure AI Developer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '64702f94-c441-49e6-a78b-ef80e0188fee')]", - "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" - } + "name": "default" }, "resources": { - "cMKKeyVault::cMKKey": { - "condition": "[and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), not(empty(tryGet(parameters('customerManagedKey'), 'keyName')))))]", + "storageAccount": { "existing": true, - "type": "Microsoft.KeyVault/vaults/keys", - "apiVersion": "2023-07-01", - "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[2]]", - "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[4]]", - "name": "[format('{0}/{1}', last(split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')), tryGet(parameters('customerManagedKey'), 'keyName'))]" + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2022-09-01", + "name": "[parameters('storageAccountName')]" }, - "avmTelemetry": { - "condition": "[parameters('enableTelemetry')]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2024-03-01", - "name": "[format('46d3xbcp.res.cognitiveservices-account.{0}.{1}', replace('0.11.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "blobServices": { + "type": "Microsoft.Storage/storageAccounts/blobServices", + "apiVersion": "2022-09-01", + "name": "[format('{0}/{1}', parameters('storageAccountName'), variables('name'))]", "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" - } - } - } - } - }, - "cMKKeyVault": { - "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId')))]", - "existing": true, - "type": "Microsoft.KeyVault/vaults", - "apiVersion": "2023-07-01", - "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[2]]", - "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[4]]", - "name": "[last(split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/'))]" - }, - "cMKUserAssignedIdentity": { - "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId')))]", - "existing": true, - "type": "Microsoft.ManagedIdentity/userAssignedIdentities", - "apiVersion": "2025-01-31-preview", - "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/')[2]]", - "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/')[4]]", - "name": "[last(split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/'))]" - }, - "cognitiveService": { - "type": "Microsoft.CognitiveServices/accounts", - "apiVersion": "2025-04-01-preview", - "name": "[parameters('name')]", - "kind": "[parameters('kind')]", - "identity": "[variables('identity')]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", - "sku": { - "name": "[parameters('sku')]" - }, - "properties": { - "allowProjectManagement": "[parameters('allowProjectManagement')]", - "customSubDomainName": "[parameters('customSubDomainName')]", - "networkAcls": "[if(not(empty(coalesce(parameters('networkAcls'), createObject()))), createObject('defaultAction', tryGet(parameters('networkAcls'), 'defaultAction'), 'virtualNetworkRules', coalesce(tryGet(parameters('networkAcls'), 'virtualNetworkRules'), createArray()), 'ipRules', coalesce(tryGet(parameters('networkAcls'), 'ipRules'), createArray())), null())]", - "publicNetworkAccess": "[if(not(equals(parameters('publicNetworkAccess'), null())), parameters('publicNetworkAccess'), if(not(empty(parameters('networkAcls'))), 'Enabled', 'Disabled'))]", - "allowedFqdnList": "[parameters('allowedFqdnList')]", - "apiProperties": "[parameters('apiProperties')]", - "disableLocalAuth": "[parameters('disableLocalAuth')]", - "encryption": "[if(not(empty(parameters('customerManagedKey'))), createObject('keySource', 'Microsoft.KeyVault', 'keyVaultProperties', createObject('identityClientId', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), ''))), reference('cMKUserAssignedIdentity').clientId, null()), 'keyVaultUri', reference('cMKKeyVault').vaultUri, 'keyName', parameters('customerManagedKey').keyName, 'keyVersion', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'keyVersion'), ''))), tryGet(parameters('customerManagedKey'), 'keyVersion'), last(split(reference('cMKKeyVault::cMKKey').keyUriWithVersion, '/'))))), null())]", - "migrationToken": "[parameters('migrationToken')]", - "restore": "[parameters('restore')]", - "restrictOutboundNetworkAccess": "[parameters('restrictOutboundNetworkAccess')]", - "userOwnedStorage": "[parameters('userOwnedStorage')]", - "dynamicThrottlingEnabled": "[parameters('dynamicThrottlingEnabled')]" - }, - "dependsOn": [ - "cMKKeyVault", - "cMKKeyVault::cMKKey", - "cMKUserAssignedIdentity" - ] - }, - "cognitiveService_deployments": { - "copy": { - "name": "cognitiveService_deployments", - "count": "[length(coalesce(parameters('deployments'), createArray()))]", - "mode": "serial", - "batchSize": 1 - }, - "type": "Microsoft.CognitiveServices/accounts/deployments", - "apiVersion": "2025-04-01-preview", - "name": "[format('{0}/{1}', parameters('name'), coalesce(tryGet(coalesce(parameters('deployments'), createArray())[copyIndex()], 'name'), format('{0}-deployments', parameters('name'))))]", - "properties": { - "model": "[coalesce(parameters('deployments'), createArray())[copyIndex()].model]", - "raiPolicyName": "[tryGet(coalesce(parameters('deployments'), createArray())[copyIndex()], 'raiPolicyName')]", - "versionUpgradeOption": "[tryGet(coalesce(parameters('deployments'), createArray())[copyIndex()], 'versionUpgradeOption')]" - }, - "sku": "[coalesce(tryGet(coalesce(parameters('deployments'), createArray())[copyIndex()], 'sku'), createObject('name', parameters('sku'), 'capacity', tryGet(parameters('sku'), 'capacity'), 'tier', tryGet(parameters('sku'), 'tier'), 'size', tryGet(parameters('sku'), 'size'), 'family', tryGet(parameters('sku'), 'family')))]", - "dependsOn": [ - "cognitiveService" - ] - }, - "cognitiveService_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.CognitiveServices/accounts/{0}', parameters('name'))]", - "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", - "properties": { - "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", - "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + "automaticSnapshotPolicyEnabled": "[parameters('automaticSnapshotPolicyEnabled')]", + "changeFeed": "[if(parameters('changeFeedEnabled'), createObject('enabled', true(), 'retentionInDays', parameters('changeFeedRetentionInDays')), null())]", + "containerDeleteRetentionPolicy": { + "enabled": "[parameters('containerDeleteRetentionPolicyEnabled')]", + "days": "[parameters('containerDeleteRetentionPolicyDays')]", + "allowPermanentDelete": "[if(equals(parameters('containerDeleteRetentionPolicyEnabled'), true()), parameters('containerDeleteRetentionPolicyAllowPermanentDelete'), null())]" + }, + "cors": "[if(not(equals(parameters('corsRules'), null())), createObject('corsRules', parameters('corsRules')), null())]", + "defaultServiceVersion": "[if(not(empty(parameters('defaultServiceVersion'))), parameters('defaultServiceVersion'), null())]", + "deleteRetentionPolicy": { + "enabled": "[parameters('deleteRetentionPolicyEnabled')]", + "days": "[parameters('deleteRetentionPolicyDays')]", + "allowPermanentDelete": "[if(and(parameters('deleteRetentionPolicyEnabled'), parameters('deleteRetentionPolicyAllowPermanentDelete')), true(), null())]" + }, + "isVersioningEnabled": "[parameters('isVersioningEnabled')]", + "lastAccessTimeTrackingPolicy": "[if(not(equals(reference('storageAccount', '2022-09-01', 'full').kind, 'Storage')), createObject('enable', parameters('lastAccessTimeTrackingPolicyEnabled'), 'name', if(equals(parameters('lastAccessTimeTrackingPolicyEnabled'), true()), 'AccessTimeTracking', null()), 'trackingGranularityInDays', if(equals(parameters('lastAccessTimeTrackingPolicyEnabled'), true()), 1, null())), null())]", + "restorePolicy": "[if(parameters('restorePolicyEnabled'), createObject('enabled', true(), 'days', parameters('restorePolicyDays')), null())]" }, "dependsOn": [ - "cognitiveService" + "storageAccount" ] }, - "cognitiveService_diagnosticSettings": { + "blobServices_diagnosticSettings": { "copy": { - "name": "cognitiveService_diagnosticSettings", + "name": "blobServices_diagnosticSettings", "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" }, "type": "Microsoft.Insights/diagnosticSettings", "apiVersion": "2021-05-01-preview", - "scope": "[format('Microsoft.CognitiveServices/accounts/{0}', parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}/blobServices/{1}', parameters('storageAccountName'), variables('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', variables('name')))]", "properties": { "copy": [ { @@ -40916,84 +32614,58 @@ "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" }, "dependsOn": [ - "cognitiveService" - ] - }, - "cognitiveService_roleAssignments": { - "copy": { - "name": "cognitiveService_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.CognitiveServices/accounts/{0}', parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.CognitiveServices/accounts', 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": [ - "cognitiveService" + "blobServices" ] }, - "cognitiveService_privateEndpoints": { + "blobServices_container": { "copy": { - "name": "cognitiveService_privateEndpoints", - "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]" + "name": "blobServices_container", + "count": "[length(coalesce(parameters('containers'), createArray()))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-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]]", + "name": "[format('{0}-Container-{1}', deployment().name, copyIndex())]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "name": { - "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'name'), format('pep-{0}-{1}-{2}', last(split(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'account'), copyIndex()))]" + "storageAccountName": { + "value": "[parameters('storageAccountName')]" }, - "privateLinkServiceConnections": "[if(not(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true())), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'account'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'account')))))), createObject('value', null()))]", - "manualPrivateLinkServiceConnections": "[if(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true()), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'account'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'account')), 'requestMessage', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualConnectionRequestMessage'), 'Manual approval required.'))))), createObject('value', null()))]", - "subnetResourceId": { - "value": "[coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId]" + "blobServiceName": { + "value": "[variables('name')]" }, - "enableTelemetry": { - "value": "[variables('enableReferencedModulesTelemetry')]" + "name": { + "value": "[coalesce(parameters('containers'), createArray())[copyIndex()].name]" }, - "location": { - "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'location'), reference(split(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location)]" + "defaultEncryptionScope": { + "value": "[tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'defaultEncryptionScope')]" }, - "lock": { - "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" + "denyEncryptionScopeOverride": { + "value": "[tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'denyEncryptionScopeOverride')]" }, - "privateDnsZoneGroup": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroup')]" + "enableNfsV3AllSquash": { + "value": "[tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'enableNfsV3AllSquash')]" }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'roleAssignments')]" + "enableNfsV3RootSquash": { + "value": "[tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'enableNfsV3RootSquash')]" }, - "tags": { - "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + "immutableStorageWithVersioningEnabled": { + "value": "[tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'immutableStorageWithVersioningEnabled')]" }, - "customDnsConfigs": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customDnsConfigs')]" + "metadata": { + "value": "[tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'metadata')]" }, - "ipConfigurations": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'ipConfigurations')]" + "publicAccess": { + "value": "[tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'publicAccess')]" }, - "applicationSecurityGroupResourceIds": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'applicationSecurityGroupResourceIds')]" + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'roleAssignments')]" }, - "customNetworkInterfaceName": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customNetworkInterfaceName')]" + "immutabilityPolicyProperties": { + "value": "[tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'immutabilityPolicyProperties')]" } }, "template": { @@ -41003,198 +32675,13 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.34.44.8038", - "templateHash": "12389807800450456797" + "version": "0.33.13.18514", + "templateHash": "10816586207828434096" }, - "name": "Private Endpoints", - "description": "This module deploys a Private Endpoint." + "name": "Storage Account Blob Containers", + "description": "This module deploys a Storage Account Blob Container." }, "definitions": { - "privateDnsZoneGroupType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the Private DNS Zone Group." - } - }, - "privateDnsZoneGroupConfigs": { - "type": "array", - "items": { - "$ref": "#/definitions/privateDnsZoneGroupConfigType" - }, - "metadata": { - "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." - } - } - }, - "metadata": { - "__bicep_export!": true - } - }, - "ipConfigurationType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the resource that is unique within a resource group." - } - }, - "properties": { - "type": "object", - "properties": { - "groupId": { - "type": "string", - "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." - } - }, - "memberName": { - "type": "string", - "metadata": { - "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." - } - }, - "privateIPAddress": { - "type": "string", - "metadata": { - "description": "Required. A private IP address obtained from the private endpoint's subnet." - } - } - }, - "metadata": { - "description": "Required. Properties of private endpoint IP configurations." - } - } - }, - "metadata": { - "__bicep_export!": true - } - }, - "privateLinkServiceConnectionType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the private link service connection." - } - }, - "properties": { - "type": "object", - "properties": { - "groupIds": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." - } - }, - "privateLinkServiceId": { - "type": "string", - "metadata": { - "description": "Required. The resource id of private link service." - } - }, - "requestMessage": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." - } - } - }, - "metadata": { - "description": "Required. Properties of private link service connection." - } - } - }, - "metadata": { - "__bicep_export!": true - } - }, - "customDnsConfigType": { - "type": "object", - "properties": { - "fqdn": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. FQDN that resolves to private endpoint IP address." - } - }, - "ipAddresses": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. A list of private IP addresses of the private endpoint." - } - } - }, - "metadata": { - "__bicep_export!": true - } - }, - "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." - } - } - }, - "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.5.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": { - "__bicep_imported_from!": { - "sourceTemplate": "private-dns-zone-group/main.bicep" - } - } - }, "roleAssignmentType": { "type": "object", "properties": { @@ -41272,118 +32759,102 @@ } }, "parameters": { - "name": { + "storageAccountName": { "type": "string", + "maxLength": 24, "metadata": { - "description": "Required. Name of the private endpoint resource to create." + "description": "Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment." } }, - "subnetResourceId": { + "blobServiceName": { "type": "string", + "defaultValue": "default", "metadata": { - "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + "description": "Optional. The name of the parent Blob Service. Required if the template is used in a standalone deployment." } }, - "applicationSecurityGroupResourceIds": { - "type": "array", - "items": { - "type": "string" - }, - "nullable": true, + "name": { + "type": "string", "metadata": { - "description": "Optional. Application security groups in which the private endpoint IP configuration is included." + "description": "Required. The name of the storage container to deploy." } }, - "customNetworkInterfaceName": { + "defaultEncryptionScope": { "type": "string", - "nullable": true, + "defaultValue": "", "metadata": { - "description": "Optional. The custom name of the network interface attached to the private endpoint." + "description": "Optional. Default the container to use specified encryption scope for all writes." } }, - "ipConfigurations": { - "type": "array", - "items": { - "$ref": "#/definitions/ipConfigurationType" - }, - "nullable": true, + "denyEncryptionScopeOverride": { + "type": "bool", + "defaultValue": false, "metadata": { - "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." + "description": "Optional. Block override of encryption scope from the container default." } }, - "privateDnsZoneGroup": { - "$ref": "#/definitions/privateDnsZoneGroupType", - "nullable": true, + "enableNfsV3AllSquash": { + "type": "bool", + "defaultValue": false, "metadata": { - "description": "Optional. The private DNS zone group to configure for the private endpoint." + "description": "Optional. Enable NFSv3 all squash on blob container." } }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]", + "enableNfsV3RootSquash": { + "type": "bool", + "defaultValue": false, "metadata": { - "description": "Optional. Location for all Resources." + "description": "Optional. Enable NFSv3 root squash on blob container." } }, - "lock": { - "$ref": "#/definitions/lockType", - "nullable": true, + "immutableStorageWithVersioningEnabled": { + "type": "bool", + "defaultValue": false, "metadata": { - "description": "Optional. The lock settings of the service." + "description": "Optional. This is an immutable property, when set to true it enables object level immutability at the container level. The property is immutable and can only be set to true at the container creation time. Existing containers must undergo a migration process." } }, - "roleAssignments": { - "type": "array", - "items": { - "$ref": "#/definitions/roleAssignmentType" - }, - "nullable": true, + "immutabilityPolicyName": { + "type": "string", + "defaultValue": "default", "metadata": { - "description": "Optional. Array of role assignments to create." + "description": "Optional. Name of the immutable policy." } }, - "tags": { + "immutabilityPolicyProperties": { "type": "object", "nullable": true, "metadata": { - "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." + "description": "Optional. Configure immutability policy." } }, - "customDnsConfigs": { - "type": "array", - "items": { - "$ref": "#/definitions/customDnsConfigType" - }, - "nullable": true, + "metadata": { + "type": "object", + "defaultValue": {}, "metadata": { - "description": "Optional. Custom DNS configurations." + "description": "Optional. A name-value pair to associate with the container as metadata." } }, - "manualPrivateLinkServiceConnections": { - "type": "array", - "items": { - "$ref": "#/definitions/privateLinkServiceConnectionType" - }, - "nullable": true, + "publicAccess": { + "type": "string", + "defaultValue": "None", + "allowedValues": [ + "Container", + "Blob", + "None" + ], "metadata": { - "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." + "description": "Optional. Specifies whether data in the container may be accessed publicly and the level of access." } }, - "privateLinkServiceConnections": { + "roleAssignments": { "type": "array", "items": { - "$ref": "#/definitions/privateLinkServiceConnectionType" + "$ref": "#/definitions/roleAssignmentType" }, "nullable": true, "metadata": { - "description": "Conditional. A grouping of information about the connection to the remote resource. Required if `manualPrivateLinkServiceConnections` is empty." - } - }, - "enableTelemetry": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Optional. Enable/Disable usage telemetry for module." + "description": "Optional. Array of role assignments to create." } } }, @@ -41397,87 +32868,56 @@ ], "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": "2024-03-01", - "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.11.0', '.', '-'), 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" - } - } - } - } + "Reader and Data Access": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c12c1c16-33a1-487b-954d-41c89c60f349')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Storage Account Backup Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e5e2a7ff-d759-4cd2-bb51-3152d37e2eb1')]", + "Storage Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab')]", + "Storage Account Key Operator Service Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '81a9662b-bebf-436f-a333-f67b29880f12')]", + "Storage Blob Data Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe')]", + "Storage Blob Data Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b7e6dc6d-f1e8-4753-8033-0f276bb0955b')]", + "Storage Blob Data Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2a2b9908-6ea1-4ae2-8e65-a410df84e7d1')]", + "Storage Blob Delegator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'db58b8e5-c6ad-4a2a-8342-4190687cbf4a')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "storageAccount::blobServices": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts/blobServices", + "apiVersion": "2022-09-01", + "name": "[format('{0}/{1}', parameters('storageAccountName'), parameters('blobServiceName'))]" }, - "privateEndpoint": { - "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2024-05-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')]" - } - } + "storageAccount": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2022-09-01", + "name": "[parameters('storageAccountName')]" }, - "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')))]", + "container": { + "type": "Microsoft.Storage/storageAccounts/blobServices/containers", + "apiVersion": "2022-09-01", + "name": "[format('{0}/{1}/{2}', parameters('storageAccountName'), parameters('blobServiceName'), parameters('name'))]", "properties": { - "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", - "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" - ] + "defaultEncryptionScope": "[if(not(empty(parameters('defaultEncryptionScope'))), parameters('defaultEncryptionScope'), null())]", + "denyEncryptionScopeOverride": "[if(equals(parameters('denyEncryptionScopeOverride'), true()), parameters('denyEncryptionScopeOverride'), null())]", + "enableNfsV3AllSquash": "[if(equals(parameters('enableNfsV3AllSquash'), true()), parameters('enableNfsV3AllSquash'), null())]", + "enableNfsV3RootSquash": "[if(equals(parameters('enableNfsV3RootSquash'), true()), parameters('enableNfsV3RootSquash'), null())]", + "immutableStorageWithVersioning": "[if(equals(parameters('immutableStorageWithVersioningEnabled'), true()), createObject('enabled', parameters('immutableStorageWithVersioningEnabled')), null())]", + "metadata": "[parameters('metadata')]", + "publicAccess": "[parameters('publicAccess')]" + } }, - "privateEndpoint_roleAssignments": { + "container_roleAssignments": { "copy": { - "name": "privateEndpoint_roleAssignments", + "name": "container_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))]", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}/blobServices/{1}/containers/{2}', parameters('storageAccountName'), parameters('blobServiceName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Storage/storageAccounts/blobServices/containers', parameters('storageAccountName'), parameters('blobServiceName'), 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]", @@ -41488,141 +32928,115 @@ "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" }, "dependsOn": [ - "privateEndpoint" + "container" ] }, - "privateEndpoint_privateDnsZoneGroup": { - "condition": "[not(empty(parameters('privateDnsZoneGroup')))]", + "immutabilityPolicy": { + "condition": "[not(empty(coalesce(parameters('immutabilityPolicyProperties'), createObject())))]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateEndpoint-PrivateDnsZoneGroup', uniqueString(deployment().name))]", + "name": "[parameters('immutabilityPolicyName')]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "name": { - "value": "[tryGet(parameters('privateDnsZoneGroup'), 'name')]" + "storageAccountName": { + "value": "[parameters('storageAccountName')]" }, - "privateEndpointName": { + "containerName": { "value": "[parameters('name')]" }, - "privateDnsZoneConfigs": { - "value": "[parameters('privateDnsZoneGroup').privateDnsZoneGroupConfigs]" + "immutabilityPeriodSinceCreationInDays": { + "value": "[tryGet(parameters('immutabilityPolicyProperties'), 'immutabilityPeriodSinceCreationInDays')]" + }, + "allowProtectedAppendWrites": { + "value": "[tryGet(parameters('immutabilityPolicyProperties'), 'allowProtectedAppendWrites')]" + }, + "allowProtectedAppendWritesAll": { + "value": "[tryGet(parameters('immutabilityPolicyProperties'), 'allowProtectedAppendWritesAll')]" } }, "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.34.44.8038", - "templateHash": "13997305779829540948" + "version": "0.33.13.18514", + "templateHash": "2769922037435749045" }, - "name": "Private Endpoint Private DNS Zone Groups", - "description": "This module deploys a Private Endpoint Private DNS Zone Group." + "name": "Storage Account Blob Container Immutability Policies", + "description": "This module deploys a Storage Account Blob Container Immutability Policy." }, - "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." - } - } - }, + "parameters": { + "storageAccountName": { + "type": "string", + "maxLength": 24, "metadata": { - "__bicep_export!": true + "description": "Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment." } - } - }, - "parameters": { - "privateEndpointName": { + }, + "containerName": { "type": "string", "metadata": { - "description": "Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment." + "description": "Conditional. The name of the parent container to apply the policy to. Required if the template is used in a standalone deployment." } }, - "privateDnsZoneConfigs": { - "type": "array", - "items": { - "$ref": "#/definitions/privateDnsZoneGroupConfigType" - }, - "minLength": 1, - "maxLength": 5, + "immutabilityPeriodSinceCreationInDays": { + "type": "int", + "defaultValue": 365, "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." + "description": "Optional. The immutability period for the blobs in the container since the policy creation, in days." } }, - "name": { - "type": "string", - "defaultValue": "default", + "allowProtectedAppendWrites": { + "type": "bool", + "defaultValue": true, "metadata": { - "description": "Optional. The name of the private DNS zone group." + "description": "Optional. This property can only be changed for unlocked time-based retention policies. When enabled, new blocks can be written to an append blob while maintaining immutability protection and compliance. Only new blocks can be added and any existing blocks cannot be modified or deleted. This property cannot be changed with ExtendImmutabilityPolicy API." } - } - }, - "variables": { - "copy": [ - { - "name": "privateDnsZoneConfigsVar", - "count": "[length(parameters('privateDnsZoneConfigs'))]", - "input": { - "name": "[coalesce(tryGet(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')], 'name'), last(split(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId, '/')))]", - "properties": { - "privateDnsZoneId": "[parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId]" - } - } + }, + "allowProtectedAppendWritesAll": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. This property can only be changed for unlocked time-based retention policies. When enabled, new blocks can be written to both \"Append and Block Blobs\" while maintaining immutability protection and compliance. Only new blocks can be added and any existing blocks cannot be modified or deleted. This property cannot be changed with ExtendImmutabilityPolicy API. The \"allowProtectedAppendWrites\" and \"allowProtectedAppendWritesAll\" properties are mutually exclusive." } - ] + } }, - "resources": { - "privateEndpoint": { - "existing": true, - "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2024-05-01", - "name": "[parameters('privateEndpointName')]" - }, - "privateDnsZoneGroup": { - "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2024-05-01", - "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", + "resources": [ + { + "type": "Microsoft.Storage/storageAccounts/blobServices/containers/immutabilityPolicies", + "apiVersion": "2022-09-01", + "name": "[format('{0}/{1}/{2}/{3}', parameters('storageAccountName'), 'default', parameters('containerName'), 'default')]", "properties": { - "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" + "immutabilityPeriodSinceCreationInDays": "[parameters('immutabilityPeriodSinceCreationInDays')]", + "allowProtectedAppendWrites": "[parameters('allowProtectedAppendWrites')]", + "allowProtectedAppendWritesAll": "[parameters('allowProtectedAppendWritesAll')]" } } - }, + ], "outputs": { "name": { "type": "string", "metadata": { - "description": "The name of the private endpoint DNS zone group." + "description": "The name of the deployed immutability policy." }, - "value": "[parameters('name')]" + "value": "default" }, "resourceId": { "type": "string", "metadata": { - "description": "The resource ID of the private endpoint DNS zone group." + "description": "The resource ID of the deployed immutability policy." }, - "value": "[resourceId('Microsoft.Network/privateEndpoints/privateDnsZoneGroups', parameters('privateEndpointName'), parameters('name'))]" + "value": "[resourceId('Microsoft.Storage/storageAccounts/blobServices/containers/immutabilityPolicies', parameters('storageAccountName'), 'default', parameters('containerName'), 'default')]" }, "resourceGroupName": { "type": "string", "metadata": { - "description": "The resource group the private endpoint DNS zone group was deployed into." + "description": "The resource group of the deployed immutability policy." }, "value": "[resourceGroup().name]" } @@ -41630,219 +33044,37 @@ } }, "dependsOn": [ - "privateEndpoint" + "container" ] } }, "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." + "description": "The name of the deployed container." }, "value": "[parameters('name')]" }, - "location": { - "type": "string", - "metadata": { - "description": "The location the resource was deployed into." - }, - "value": "[reference('privateEndpoint', '2024-05-01', 'full').location]" - }, - "customDnsConfigs": { - "type": "array", - "items": { - "$ref": "#/definitions/customDnsConfigType" - }, - "metadata": { - "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": { + "resourceId": { "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": [ - "cognitiveService" - ] - }, - "secretsExport": { - "condition": "[not(equals(parameters('secretsExportConfiguration'), null()))]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-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]]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "keyVaultName": { - "value": "[last(split(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '/'))]" - }, - "secretsToSet": { - "value": "[union(createArray(), if(contains(parameters('secretsExportConfiguration'), 'accessKey1Name'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'accessKey1Name'), 'value', listKeys('cognitiveService', '2025-04-01-preview').key1)), createArray()), if(contains(parameters('secretsExportConfiguration'), 'accessKey2Name'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'accessKey2Name'), 'value', listKeys('cognitiveService', '2025-04-01-preview').key2)), createArray()))]" - } - }, - "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.1.42791", - "templateHash": "1200612323329026557" - } - }, - "definitions": { - "secretSetOutputType": { - "type": "object", - "properties": { - "secretResourceId": { - "type": "string", - "metadata": { - "description": "The resourceId of the exported secret." - } - }, - "secretUri": { - "type": "string", - "metadata": { - "description": "The secret URI of the exported secret." - } - }, - "secretUriWithVersion": { - "type": "string", - "metadata": { - "description": "The secret URI with version of the exported secret." - } - } + "description": "The resource ID of the deployed container." }, - "metadata": { - "description": "An AVM-aligned type for the output of the secret set via the secrets export feature.", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } - } + "value": "[resourceId('Microsoft.Storage/storageAccounts/blobServices/containers', parameters('storageAccountName'), parameters('blobServiceName'), parameters('name'))]" }, - "secretToSetType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the secret to set." - } - }, - "value": { - "type": "securestring", - "metadata": { - "description": "Required. The value of the secret to set." - } - } - }, - "metadata": { - "description": "An AVM-aligned type for the secret to set via the secrets export feature.", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } - } - } - }, - "parameters": { - "keyVaultName": { + "resourceGroupName": { "type": "string", "metadata": { - "description": "Required. The name of the Key Vault to set the ecrets in." - } - }, - "secretsToSet": { - "type": "array", - "items": { - "$ref": "#/definitions/secretToSetType" - }, - "metadata": { - "description": "Required. The secrets to set in the Key Vault." - } - } - }, - "resources": { - "keyVault": { - "existing": true, - "type": "Microsoft.KeyVault/vaults", - "apiVersion": "2023-07-01", - "name": "[parameters('keyVaultName')]" - }, - "secrets": { - "copy": { - "name": "secrets", - "count": "[length(parameters('secretsToSet'))]" - }, - "type": "Microsoft.KeyVault/vaults/secrets", - "apiVersion": "2023-07-01", - "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('secretsToSet')[copyIndex()].name)]", - "properties": { - "value": "[parameters('secretsToSet')[copyIndex()].value]" - } - } - }, - "outputs": { - "secretsSet": { - "type": "array", - "items": { - "$ref": "#/definitions/secretSetOutputType" - }, - "metadata": { - "description": "The references to the secrets exported to the provided Key Vault." + "description": "The resource group of the deployed container." }, - "copy": { - "count": "[length(range(0, length(coalesce(parameters('secretsToSet'), createArray()))))]", - "input": { - "secretResourceId": "[resourceId('Microsoft.KeyVault/vaults/secrets', parameters('keyVaultName'), parameters('secretsToSet')[range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()]].name)]", - "secretUri": "[reference(format('secrets[{0}]', range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()])).secretUri]", - "secretUriWithVersion": "[reference(format('secrets[{0}]', range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()])).secretUriWithVersion]" - } - } + "value": "[resourceGroup().name]" } } } }, "dependsOn": [ - "cognitiveService" + "blobServices" ] } }, @@ -41850,557 +33082,60 @@ "name": { "type": "string", "metadata": { - "description": "The name of the cognitive services account." + "description": "The name of the deployed blob service." }, - "value": "[parameters('name')]" + "value": "[variables('name')]" }, "resourceId": { "type": "string", "metadata": { - "description": "The resource ID of the cognitive services account." + "description": "The resource ID of the deployed blob service." }, - "value": "[resourceId('Microsoft.CognitiveServices/accounts', parameters('name'))]" + "value": "[resourceId('Microsoft.Storage/storageAccounts/blobServices', parameters('storageAccountName'), variables('name'))]" }, "resourceGroupName": { "type": "string", "metadata": { - "description": "The resource group the cognitive services account was deployed into." + "description": "The name of the deployed blob service." }, "value": "[resourceGroup().name]" - }, - "endpoint": { - "type": "string", - "metadata": { - "description": "The service endpoint of the cognitive services account." - }, - "value": "[reference('cognitiveService').endpoint]" - }, - "endpoints": { - "$ref": "#/definitions/endpointType", - "metadata": { - "description": "All endpoints available for the cognitive services account, types depends on the cognitive service kind." - }, - "value": "[reference('cognitiveService').endpoints]" - }, - "systemAssignedMIPrincipalId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "The principal ID of the system assigned identity." - }, - "value": "[tryGet(tryGet(reference('cognitiveService', '2025-04-01-preview', 'full'), 'identity'), 'principalId')]" - }, - "location": { - "type": "string", - "metadata": { - "description": "The location the resource was deployed into." - }, - "value": "[reference('cognitiveService', '2025-04-01-preview', 'full').location]" - }, - "exportedSecrets": { - "$ref": "#/definitions/secretsOutputType", - "metadata": { - "description": "A hashtable of references to the secrets exported to the provided Key Vault. The key of each reference is each secret's name." - }, - "value": "[if(not(equals(parameters('secretsExportConfiguration'), null())), toObject(reference('secretsExport').outputs.secretsSet.value, lambda('secret', last(split(lambdaVariables('secret').secretResourceId, '/'))), lambda('secret', lambdaVariables('secret'))), createObject())]" - }, - "privateEndpoints": { - "type": "array", - "items": { - "$ref": "#/definitions/privateEndpointOutputType" - }, - "metadata": { - "description": "The private endpoints of the congitive services account." - }, - "copy": { - "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]", - "input": { - "name": "[reference(format('cognitiveService_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", - "resourceId": "[reference(format('cognitiveService_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", - "groupId": "[tryGet(tryGet(reference(format('cognitiveService_privateEndpoints[{0}]', copyIndex())).outputs, 'groupId'), 'value')]", - "customDnsConfigs": "[reference(format('cognitiveService_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfigs.value]", - "networkInterfaceResourceIds": "[reference(format('cognitiveService_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceResourceIds.value]" - } - } } } } - } - } - }, - "outputs": { - "resourceId": { - "type": "string", - "value": "[reference('cognitiveService').outputs.resourceId.value]" + }, + "dependsOn": [ + "storageAccount" + ] }, - "name": { - "type": "string", - "value": "[reference('cognitiveService').outputs.name.value]" - }, - "systemAssignedMIPrincipalId": { - "type": "string", - "nullable": true, - "value": "[tryGet(tryGet(reference('cognitiveService').outputs, 'systemAssignedMIPrincipalId'), 'value')]" - }, - "endpoint": { - "type": "string", - "value": "[reference('cognitiveService').outputs.endpoint.value]" - }, - "foundryConnection": { - "type": "object", - "value": { - "name": "[reference('cognitiveService').outputs.name.value]", - "value": null, - "category": "[parameters('category')]", - "target": "[reference('cognitiveService').outputs.endpoint.value]", - "kind": "[parameters('kind')]", - "connectionProperties": { - "authType": "AAD" - }, - "isSharedToAll": true, - "metadata": { - "ApiType": "Azure", - "Kind": "[parameters('kind')]", - "ResourceId": "[reference('cognitiveService').outputs.resourceId.value]" - } - } - } - } - } - }, - "dependsOn": [ - "cognitiveServicesPrivateDnsZone" - ] - }, - "vision": { - "condition": "[parameters('visionEnabled')]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[take(format('{0}-vision-deployment', parameters('name')), 64)]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[format('vision{0}{1}', parameters('name'), parameters('resourceToken'))]" - }, - "location": { - "value": "[parameters('location')]" - }, - "kind": { - "value": "ComputerVision" - }, - "sku": { - "value": "S1" - }, - "networkIsolation": { - "value": "[parameters('networkIsolation')]" - }, - "networkAcls": { - "value": "[parameters('networkAcls')]" - }, - "virtualNetworkSubnetResourceId": "[if(parameters('networkIsolation'), createObject('value', parameters('virtualNetworkSubnetResourceId')), createObject('value', ''))]", - "privateDnsZonesResourceIds": "[if(parameters('networkIsolation'), createObject('value', createArray(reference('cognitiveServicesPrivateDnsZone').outputs.resourceId.value)), createObject('value', createArray()))]", - "logAnalyticsWorkspaceResourceId": { - "value": "[parameters('logAnalyticsWorkspaceResourceId')]" - }, - "tags": { - "value": "[parameters('tags')]" - } - }, - "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.1.42791", - "templateHash": "2348080591288311162" - } - }, - "definitions": { - "deploymentsType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Specify the name of cognitive service account deployment." - } - }, - "model": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of Cognitive Services account deployment model." - } - }, - "format": { - "type": "string", - "metadata": { - "description": "Required. The format of Cognitive Services account deployment model." - } - }, - "version": { - "type": "string", - "metadata": { - "description": "Required. The version of Cognitive Services account deployment model." - } - } - }, - "metadata": { - "description": "Required. Properties of Cognitive Services account deployment model." - } - }, - "sku": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the resource model definition representing SKU." - } - }, - "capacity": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The capacity of the resource model definition representing SKU." - } - }, - "tier": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The tier of the resource model definition representing SKU." - } - }, - "size": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The size of the resource model definition representing SKU." - } - }, - "family": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The family of the resource model definition representing SKU." - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. The resource model definition representing SKU." - } - }, - "raiPolicyName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of RAI policy." - } - }, - "versionUpgradeOption": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The version upgrade option." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "../customTypes.bicep" - } - } - }, - "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.5.1" - } - } - } - }, - "parameters": { - "name": { - "type": "string", - "metadata": { - "description": "Name of the Cognitive Services resource. Must be unique in the resource group." - } - }, - "location": { - "type": "string", - "metadata": { - "description": "The location of the Cognitive Services resource." - } - }, - "kind": { - "type": "string", - "allowedValues": [ - "AIServices", - "AnomalyDetector", - "CognitiveServices", - "ComputerVision", - "ContentModerator", - "ContentSafety", - "ConversationalLanguageUnderstanding", - "CustomVision.Prediction", - "CustomVision.Training", - "Face", - "FormRecognizer", - "HealthInsights", - "ImmersiveReader", - "Internal.AllInOne", - "LUIS", - "LUIS.Authoring", - "LanguageAuthoring", - "MetricsAdvisor", - "OpenAI", - "Personalizer", - "QnAMaker.v2", - "SpeechServices", - "TextAnalytics", - "TextTranslation" - ], - "metadata": { - "description": "Required. Kind of the Cognitive Services account. Use 'Get-AzCognitiveServicesAccountSku' to determine a valid combinations of 'kind' and 'SKU' for your Azure region." - } - }, - "sku": { - "type": "string", - "defaultValue": "S0", - "allowedValues": [ - "S", - "S0", - "S1", - "S2", - "S3", - "S4", - "S5", - "S6", - "S7", - "S8" - ], - "metadata": { - "description": "Required. The SKU of the Cognitive Services account. Use 'Get-AzCognitiveServicesAccountSku' to determine a valid combinations of 'kind' and 'SKU' for your Azure region." - } - }, - "category": { - "type": "string", - "defaultValue": "CognitiveService", - "metadata": { - "description": "Category of the Cognitive Services account." - } - }, - "networkIsolation": { - "type": "bool", - "metadata": { - "description": "Specifies whether to enable network isolation. If true, the resource will be deployed in a private endpoint and public network access will be disabled." - } - }, - "privateDnsZonesResourceIds": { - "type": "array", - "items": { - "type": "string" - }, - "defaultValue": [], - "metadata": { - "description": "Existing resource ID of the private DNS zone for the private endpoint." - } - }, - "virtualNetworkSubnetResourceId": { - "type": "string", - "metadata": { - "description": "Resource ID of the subnet for the private endpoint." - } - }, - "logAnalyticsWorkspaceResourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the Log Analytics workspace to use for diagnostic settings." - } - }, - "aiModelDeployments": { - "type": "array", - "items": { - "$ref": "#/definitions/deploymentsType" - }, - "defaultValue": [], - "metadata": { - "description": "Optional. Specifies the OpenAI deployments to create." - } - }, - "roleAssignments": { - "type": "array", - "items": { - "$ref": "#/definitions/roleAssignmentType" - }, - "nullable": true, - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "tags": { - "type": "object", - "defaultValue": {}, - "metadata": { - "description": "Optional. Tags to be applied to the resources." - } - }, - "networkAcls": { - "type": "object", - "metadata": { - "description": "Optional. A collection of rules governing the accessibility from specific network locations." - } - } - }, - "variables": { - "copy": [ - { - "name": "privateDnsZones", - "count": "[length(parameters('privateDnsZonesResourceIds'))]", - "input": { - "privateDnsZoneResourceId": "[parameters('privateDnsZonesResourceIds')[copyIndex('privateDnsZones')]]" - } - } - ], - "nameFormatted": "[take(toLower(parameters('name')), 24)]" - }, - "resources": { - "cognitiveService": { + "storageAccount_fileServices": { + "condition": "[not(empty(parameters('fileServices')))]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[take(format('cog-{0}-{1}-deployment', parameters('kind'), parameters('name')), 64)]", + "name": "[format('{0}-Storage-FileServices', uniqueString(deployment().name, parameters('location')))]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "name": { - "value": "[variables('nameFormatted')]" - }, - "location": { - "value": "[parameters('location')]" - }, - "tags": { - "value": "[parameters('tags')]" - }, - "sku": { - "value": "[parameters('sku')]" - }, - "kind": { - "value": "[parameters('kind')]" - }, - "allowProjectManagement": { - "value": true - }, - "managedIdentities": { - "value": { - "systemAssigned": true - } - }, - "deployments": { - "value": "[parameters('aiModelDeployments')]" - }, - "customSubDomainName": { + "storageAccountName": { "value": "[parameters('name')]" }, - "disableLocalAuth": { - "value": "[parameters('networkIsolation')]" - }, - "publicNetworkAccess": "[if(parameters('networkIsolation'), createObject('value', 'Disabled'), createObject('value', 'Enabled'))]", "diagnosticSettings": { - "value": [ - { - "workspaceResourceId": "[parameters('logAnalyticsWorkspaceResourceId')]" - } - ] + "value": "[tryGet(parameters('fileServices'), 'diagnosticSettings')]" }, - "roleAssignments": { - "value": "[parameters('roleAssignments')]" + "protocolSettings": { + "value": "[tryGet(parameters('fileServices'), 'protocolSettings')]" }, - "networkAcls": { - "value": "[parameters('networkAcls')]" + "shareDeleteRetentionPolicy": { + "value": "[tryGet(parameters('fileServices'), 'shareDeleteRetentionPolicy')]" }, - "privateEndpoints": "[if(parameters('networkIsolation'), createObject('value', createArray(createObject('privateDnsZoneGroup', createObject('privateDnsZoneGroupConfigs', variables('privateDnsZones')), 'subnetResourceId', parameters('virtualNetworkSubnetResourceId')))), createObject('value', createArray()))]" + "shares": { + "value": "[tryGet(parameters('fileServices'), 'shares')]" + }, + "corsRules": { + "value": "[tryGet(parameters('queueServices'), 'corsRules')]" + } }, "template": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", @@ -42409,401 +33144,71 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "16135659971302525380" + "version": "0.33.13.18514", + "templateHash": "987643333058038389" }, - "name": "Cognitive Services", - "description": "This module deploys a Cognitive Service." + "name": "Storage Account File Share Services", + "description": "This module deploys a Storage Account File Share Service." }, "definitions": { - "privateEndpointOutputType": { + "corsRuleType": { "type": "object", "properties": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the private endpoint." - } - }, - "resourceId": { - "type": "string", + "allowedHeaders": { + "type": "array", + "items": { + "type": "string" + }, "metadata": { - "description": "The resource ID of the private endpoint." + "description": "Required. A list of headers allowed to be part of the cross-origin request." } }, - "groupId": { - "type": "string", - "nullable": true, + "allowedMethods": { + "type": "array", + "allowedValues": [ + "CONNECT", + "DELETE", + "GET", + "HEAD", + "MERGE", + "OPTIONS", + "PATCH", + "POST", + "PUT", + "TRACE" + ], "metadata": { - "description": "The group Id for the private endpoint Group." + "description": "Required. A list of HTTP methods that are allowed to be executed by the origin." } }, - "customDnsConfigs": { - "type": "array", - "items": { - "type": "object", - "properties": { - "fqdn": { - "type": "string", - "nullable": true, - "metadata": { - "description": "FQDN that resolves to private endpoint IP address." - } - }, - "ipAddresses": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "A list of private IP addresses of the private endpoint." - } - } - } - }, - "metadata": { - "description": "The custom DNS configurations of the private endpoint." - } - }, - "networkInterfaceResourceIds": { + "allowedOrigins": { "type": "array", "items": { "type": "string" }, "metadata": { - "description": "The IDs of the network interfaces associated with the private endpoint." - } - } - }, - "metadata": { - "__bicep_export!": true, - "description": "The type for the private endpoint output." - } - }, - "deploymentType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Specify the name of cognitive service account deployment." - } - }, - "model": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of Cognitive Services account deployment model." - } - }, - "format": { - "type": "string", - "metadata": { - "description": "Required. The format of Cognitive Services account deployment model." - } - }, - "version": { - "type": "string", - "metadata": { - "description": "Required. The version of Cognitive Services account deployment model." - } - } - }, - "metadata": { - "description": "Required. Properties of Cognitive Services account deployment model." - } - }, - "sku": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the resource model definition representing SKU." - } - }, - "capacity": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The capacity of the resource model definition representing SKU." - } - }, - "tier": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The tier of the resource model definition representing SKU." - } - }, - "size": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The size of the resource model definition representing SKU." - } - }, - "family": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The family of the resource model definition representing SKU." - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. The resource model definition representing SKU." - } - }, - "raiPolicyName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of RAI policy." - } - }, - "versionUpgradeOption": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The version upgrade option." - } - } - }, - "metadata": { - "__bicep_export!": true, - "description": "The type for a cognitive services account deployment." - } - }, - "endpointType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Type of the endpoint." - } - }, - "endpoint": { - "type": "string", - "nullable": true, - "metadata": { - "description": "The endpoint URI." - } - } - }, - "metadata": { - "__bicep_export!": true, - "description": "The type for a cognitive services account endpoint." - } - }, - "secretsExportConfigurationType": { - "type": "object", - "properties": { - "keyVaultResourceId": { - "type": "string", - "metadata": { - "description": "Required. The key vault name where to store the keys and connection strings generated by the modules." - } - }, - "accessKey1Name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name for the accessKey1 secret to create." - } - }, - "accessKey2Name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name for the accessKey2 secret to create." - } - } - }, - "metadata": { - "__bicep_export!": true, - "description": "The type of the secrets exported to the provided Key Vault." - } - }, - "_1.privateEndpointCustomDnsConfigType": { - "type": "object", - "properties": { - "fqdn": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. FQDN that resolves to private endpoint IP address." + "description": "Required. A list of origin domains that will be allowed via CORS, or \"*\" to allow all domains." } }, - "ipAddresses": { + "exposedHeaders": { "type": "array", "items": { "type": "string" }, "metadata": { - "description": "Required. A list of private IP addresses of the private endpoint." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } - } - }, - "_1.privateEndpointIpConfigurationType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the resource that is unique within a resource group." - } - }, - "properties": { - "type": "object", - "properties": { - "groupId": { - "type": "string", - "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." - } - }, - "memberName": { - "type": "string", - "metadata": { - "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to." - } - }, - "privateIPAddress": { - "type": "string", - "metadata": { - "description": "Required. A private IP address obtained from the private endpoint's subnet." - } - } - }, - "metadata": { - "description": "Required. Properties of private endpoint IP configurations." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } - } - }, - "_1.privateEndpointPrivateDnsZoneGroupType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the Private DNS Zone Group." - } - }, - "privateDnsZoneGroupConfigs": { - "type": "array", - "items": { - "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": "Required. The private DNS Zone Groups to associate the Private Endpoint. A DNS Zone Group can support up to 5 DNS zones." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } - } - }, - "_1.secretSetOutputType": { - "type": "object", - "properties": { - "secretResourceId": { - "type": "string", - "metadata": { - "description": "The resourceId of the exported secret." - } - }, - "secretUri": { - "type": "string", - "metadata": { - "description": "The secret URI of the exported secret." - } - }, - "secretUriWithVersion": { - "type": "string", - "metadata": { - "description": "The secret URI with version of the exported secret." - } - } - }, - "metadata": { - "description": "An AVM-aligned type for the output of the secret set via the secrets export feature.", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } - } - }, - "customerManagedKeyType": { - "type": "object", - "properties": { - "keyVaultResourceId": { - "type": "string", - "metadata": { - "description": "Required. The resource ID of a key vault to reference a customer managed key for encryption from." - } - }, - "keyName": { - "type": "string", - "metadata": { - "description": "Required. The name of the customer managed key to use for encryption." - } - }, - "keyVersion": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The version of the customer managed key to reference for encryption. If not provided, the deployment will use the latest version available at deployment time." + "description": "Required. A list of response headers to expose to CORS clients." } }, - "userAssignedIdentityResourceId": { - "type": "string", - "nullable": true, + "maxAgeInSeconds": { + "type": "int", "metadata": { - "description": "Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use." + "description": "Required. The number of seconds that the client/browser should cache a preflight response." } } }, "metadata": { - "description": "An AVM-aligned type for a customer-managed key. To be used if the resource type does not support auto-rotation of the customer-managed key.", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } + "__bicep_export!": true, + "description": "The type for a cors rule." } }, "diagnosticSettingFullType": { @@ -42927,1017 +33332,179 @@ "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } + } + }, + "parameters": { + "storageAccountName": { + "type": "string", + "maxLength": 24, + "metadata": { + "description": "Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment." + } }, - "lockType": { + "name": { + "type": "string", + "defaultValue": "default", + "metadata": { + "description": "Optional. The name of the file service." + } + }, + "protocolSettings": { "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." - } - } - }, + "defaultValue": {}, "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.5.1" - } + "description": "Optional. Protocol settings for file service." } }, - "managedIdentityAllType": { + "shareDeleteRetentionPolicy": { "type": "object", - "properties": { - "systemAssigned": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Enables system assigned managed identity on the resource." - } - }, - "userAssignedResourceIds": { - "type": "array", - "items": { - "type": "string" - }, - "nullable": true, - "metadata": { - "description": "Optional. The resource ID(s) to assign to the resource. Required if a user assigned identity is used for encryption." - } - } + "defaultValue": { + "enabled": true, + "days": 7 }, "metadata": { - "description": "An AVM-aligned type for a managed identity configuration. To be used if both a system-assigned & user-assigned identities are supported by the resource provider.", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } + "description": "Optional. The service properties for soft delete." } }, - "privateEndpointSingleServiceType": { - "type": "object", + "corsRules": { + "type": "array", + "items": { + "$ref": "#/definitions/corsRuleType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The List of CORS rules. You can include up to five CorsRule elements in the request." + } + }, + "diagnosticSettings": { + "type": "array", + "items": { + "$ref": "#/definitions/diagnosticSettingFullType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "shares": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. File shares to create." + } + } + }, + "resources": { + "storageAccount": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2023-04-01", + "name": "[parameters('storageAccountName')]" + }, + "fileServices": { + "type": "Microsoft.Storage/storageAccounts/fileServices", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}', parameters('storageAccountName'), parameters('name'))]", "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the Private Endpoint." - } - }, - "location": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The location to deploy the Private Endpoint to." - } - }, - "privateLinkServiceConnectionName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the private link connection to create." - } - }, - "service": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The subresource to deploy the Private Endpoint for. For example \"vault\" for a Key Vault Private Endpoint." - } - }, - "subnetResourceId": { - "type": "string", - "metadata": { - "description": "Required. Resource ID of the subnet where the endpoint needs to be created." - } - }, - "resourceGroupResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The resource ID of the Resource Group the Private Endpoint will be created in. If not specified, the Resource Group of the provided Virtual Network Subnet is used." - } - }, - "privateDnsZoneGroup": { - "$ref": "#/definitions/_1.privateEndpointPrivateDnsZoneGroupType", - "nullable": true, - "metadata": { - "description": "Optional. The private DNS Zone Group to configure for the Private Endpoint." - } - }, - "isManualConnection": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. If Manual Private Link Connection is required." - } - }, - "manualConnectionRequestMessage": { - "type": "string", - "nullable": true, - "maxLength": 140, - "metadata": { - "description": "Optional. A message passed to the owner of the remote resource with the manual connection request." + "cors": "[if(not(equals(parameters('corsRules'), null())), createObject('corsRules', parameters('corsRules')), null())]", + "protocolSettings": "[parameters('protocolSettings')]", + "shareDeleteRetentionPolicy": "[parameters('shareDeleteRetentionPolicy')]" + } + }, + "fileServices_diagnosticSettings": { + "copy": { + "name": "fileServices_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}/fileServices/{1}', parameters('storageAccountName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null + } + }, + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "fileServices" + ] + }, + "fileServices_shares": { + "copy": { + "name": "fileServices_shares", + "count": "[length(coalesce(parameters('shares'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-shares-{1}', deployment().name, copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" }, - "customDnsConfigs": { - "type": "array", - "items": { - "$ref": "#/definitions/_1.privateEndpointCustomDnsConfigType" + "mode": "Incremental", + "parameters": { + "storageAccountName": { + "value": "[parameters('storageAccountName')]" }, - "nullable": true, - "metadata": { - "description": "Optional. Custom DNS configurations." - } - }, - "ipConfigurations": { - "type": "array", - "items": { - "$ref": "#/definitions/_1.privateEndpointIpConfigurationType" + "fileServicesName": { + "value": "[parameters('name')]" }, - "nullable": true, - "metadata": { - "description": "Optional. A list of IP configurations of the Private Endpoint. This will be used to map to the first-party Service endpoints." - } - }, - "applicationSecurityGroupResourceIds": { - "type": "array", - "items": { - "type": "string" + "name": { + "value": "[coalesce(parameters('shares'), createArray())[copyIndex()].name]" }, - "nullable": true, - "metadata": { - "description": "Optional. Application security groups in which the Private Endpoint IP configuration is included." - } - }, - "customNetworkInterfaceName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The custom name of the network interface attached to the Private Endpoint." - } - }, - "lock": { - "$ref": "#/definitions/lockType", - "nullable": true, - "metadata": { - "description": "Optional. Specify the type of lock." - } - }, - "roleAssignments": { - "type": "array", - "items": { - "$ref": "#/definitions/roleAssignmentType" + "accessTier": { + "value": "[coalesce(tryGet(coalesce(parameters('shares'), createArray())[copyIndex()], 'accessTier'), if(equals(reference('storageAccount', '2023-04-01', 'full').kind, 'FileStorage'), 'Premium', 'TransactionOptimized'))]" }, - "nullable": true, - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "tags": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Tags to be applied on all resources/Resource Groups in this deployment." - } - }, - "enableTelemetry": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Enable/Disable usage telemetry for module." - } - } - }, - "metadata": { - "description": "An AVM-aligned type for a private endpoint. To be used if the private endpoint's default service / groupId can be assumed (i.e., for services that only have one Private Endpoint type like 'vault' for key vault).", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } - } - }, - "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." + "enabledProtocols": { + "value": "[tryGet(coalesce(parameters('shares'), createArray())[copyIndex()], 'enabledProtocols')]" + }, + "rootSquash": { + "value": "[tryGet(coalesce(parameters('shares'), createArray())[copyIndex()], 'rootSquash')]" + }, + "shareQuota": { + "value": "[tryGet(coalesce(parameters('shares'), createArray())[copyIndex()], 'shareQuota')]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('shares'), createArray())[copyIndex()], 'roleAssignments')]" } }, - "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.5.1" - } - } - }, - "secretsOutputType": { - "type": "object", - "properties": {}, - "additionalProperties": { - "$ref": "#/definitions/_1.secretSetOutputType", - "metadata": { - "description": "An exported secret's references." - } - }, - "metadata": { - "description": "A map of the exported secrets", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } - } - } - }, - "parameters": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of Cognitive Services account." - } - }, - "kind": { - "type": "string", - "allowedValues": [ - "AIServices", - "AnomalyDetector", - "CognitiveServices", - "ComputerVision", - "ContentModerator", - "ContentSafety", - "ConversationalLanguageUnderstanding", - "CustomVision.Prediction", - "CustomVision.Training", - "Face", - "FormRecognizer", - "HealthInsights", - "ImmersiveReader", - "Internal.AllInOne", - "LUIS", - "LUIS.Authoring", - "LanguageAuthoring", - "MetricsAdvisor", - "OpenAI", - "Personalizer", - "QnAMaker.v2", - "SpeechServices", - "TextAnalytics", - "TextTranslation" - ], - "metadata": { - "description": "Required. Kind of the Cognitive Services account. Use 'Get-AzCognitiveServicesAccountSku' to determine a valid combinations of 'kind' and 'SKU' for your Azure region." - } - }, - "sku": { - "type": "string", - "defaultValue": "S0", - "allowedValues": [ - "C2", - "C3", - "C4", - "F0", - "F1", - "S", - "S0", - "S1", - "S10", - "S2", - "S3", - "S4", - "S5", - "S6", - "S7", - "S8", - "S9" - ], - "metadata": { - "description": "Optional. SKU of the Cognitive Services account. Use 'Get-AzCognitiveServicesAccountSku' to determine a valid combinations of 'kind' and 'SKU' for your Azure region." - } - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]", - "metadata": { - "description": "Optional. Location for all Resources." - } - }, - "diagnosticSettings": { - "type": "array", - "items": { - "$ref": "#/definitions/diagnosticSettingFullType" - }, - "nullable": true, - "metadata": { - "description": "Optional. The diagnostic settings of the service." - } - }, - "publicNetworkAccess": { - "type": "string", - "nullable": true, - "allowedValues": [ - "Enabled", - "Disabled" - ], - "metadata": { - "description": "Optional. Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set and networkAcls are not set." - } - }, - "customSubDomainName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Conditional. Subdomain name used for token-based authentication. Required if 'networkAcls' or 'privateEndpoints' are set." - } - }, - "networkAcls": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. A collection of rules governing the accessibility from specific network locations." - } - }, - "privateEndpoints": { - "type": "array", - "items": { - "$ref": "#/definitions/privateEndpointSingleServiceType" - }, - "nullable": true, - "metadata": { - "description": "Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible." - } - }, - "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", - "nullable": true, - "metadata": { - "description": "Optional. Tags of the resource." - } - }, - "allowedFqdnList": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. List of allowed FQDN." - } - }, - "apiProperties": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The API properties for special APIs." - } - }, - "disableLocalAuth": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Optional. Allow only Azure AD authentication. Should be enabled for security reasons." - } - }, - "customerManagedKey": { - "$ref": "#/definitions/customerManagedKeyType", - "nullable": true, - "metadata": { - "description": "Optional. The customer managed key definition." - } - }, - "dynamicThrottlingEnabled": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "Optional. The flag to enable dynamic throttling." - } - }, - "migrationToken": { - "type": "securestring", - "nullable": true, - "metadata": { - "description": "Optional. Resource migration token." - } - }, - "restore": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "Optional. Restore a soft-deleted cognitive service at deployment time. Will fail if no such soft-deleted resource exists." - } - }, - "restrictOutboundNetworkAccess": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Optional. Restrict outbound network access." - } - }, - "userOwnedStorage": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. The storage accounts for this resource." - } - }, - "managedIdentities": { - "$ref": "#/definitions/managedIdentityAllType", - "nullable": true, - "metadata": { - "description": "Optional. The managed identity definition for this resource." - } - }, - "enableTelemetry": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Optional. Enable/Disable usage telemetry for module." - } - }, - "deployments": { - "type": "array", - "items": { - "$ref": "#/definitions/deploymentType" - }, - "nullable": true, - "metadata": { - "description": "Optional. Array of deployments about cognitive service accounts to create." - } - }, - "secretsExportConfiguration": { - "$ref": "#/definitions/secretsExportConfigurationType", - "nullable": true, - "metadata": { - "description": "Optional. Key vault reference and secret settings for the module's secrets export." - } - }, - "allowProjectManagement": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Enable/Disable project management feature for AI Foundry." - } - } - }, - "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)))))]" - } - ], - "enableReferencedModulesTelemetry": false, - "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", - "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned, UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', null())), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", - "builtInRoleNames": { - "Cognitive Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '25fbc0a9-bd7c-42a3-aa1a-3b75d497ee68')]", - "Cognitive Services Custom Vision Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c1ff6cc2-c111-46fe-8896-e0ef812ad9f3')]", - "Cognitive Services Custom Vision Deployment": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5c4089e1-6d96-4d2f-b296-c1bc7137275f')]", - "Cognitive Services Custom Vision Labeler": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '88424f51-ebe7-446f-bc41-7fa16989e96c')]", - "Cognitive Services Custom Vision Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '93586559-c37d-4a6b-ba08-b9f0940c2d73')]", - "Cognitive Services Custom Vision Trainer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0a5ae4ab-0d65-4eeb-be61-29fc9b54394b')]", - "Cognitive Services Data Reader (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b59867f0-fa02-499b-be73-45a86b5b3e1c')]", - "Cognitive Services Face Recognizer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9894cab4-e18a-44aa-828b-cb588cd6f2d7')]", - "Cognitive Services Immersive Reader User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b2de6794-95db-4659-8781-7e080d3f2b9d')]", - "Cognitive Services Language Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f07febfe-79bc-46b1-8b37-790e26e6e498')]", - "Cognitive Services Language Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7628b7b8-a8b2-4cdc-b46f-e9b35248918e')]", - "Cognitive Services Language Writer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f2310ca1-dc64-4889-bb49-c8e0fa3d47a8')]", - "Cognitive Services LUIS Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f72c8140-2111-481c-87ff-72b910f6e3f8')]", - "Cognitive Services LUIS Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18e81cdc-4e98-4e29-a639-e7d10c5a6226')]", - "Cognitive Services LUIS Writer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6322a993-d5c9-4bed-b113-e49bbea25b27')]", - "Cognitive Services Metrics Advisor Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'cb43c632-a144-4ec5-977c-e80c4affc34a')]", - "Cognitive Services Metrics Advisor User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3b20f47b-3825-43cb-8114-4bd2201156a8')]", - "Cognitive Services OpenAI Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a001fd3d-188f-4b5d-821b-7da978bf7442')]", - "Cognitive Services OpenAI User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e0bd9bd-7b93-4f28-af87-19fc36ad61bd')]", - "Cognitive Services QnA Maker Editor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f4cc2bf9-21be-47a1-bdf1-5c5804381025')]", - "Cognitive Services QnA Maker Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '466ccd10-b268-4a11-b098-b4849f024126')]", - "Cognitive Services Speech Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0e75ca1e-0464-4b4d-8b93-68208a576181')]", - "Cognitive Services Speech User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f2dc8367-1007-4938-bd23-fe263f013447')]", - "Cognitive Services User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a97b65f3-24c7-4388-baec-2e87135dc908')]", - "Azure AI Developer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '64702f94-c441-49e6-a78b-ef80e0188fee')]", - "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" - } - }, - "resources": { - "cMKKeyVault::cMKKey": { - "condition": "[and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), not(empty(tryGet(parameters('customerManagedKey'), 'keyName')))))]", - "existing": true, - "type": "Microsoft.KeyVault/vaults/keys", - "apiVersion": "2023-07-01", - "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[2]]", - "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[4]]", - "name": "[format('{0}/{1}', last(split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')), tryGet(parameters('customerManagedKey'), 'keyName'))]" - }, - "avmTelemetry": { - "condition": "[parameters('enableTelemetry')]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2024-03-01", - "name": "[format('46d3xbcp.res.cognitiveservices-account.{0}.{1}', replace('0.11.0', '.', '-'), 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" - } - } - } - } - }, - "cMKKeyVault": { - "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId')))]", - "existing": true, - "type": "Microsoft.KeyVault/vaults", - "apiVersion": "2023-07-01", - "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[2]]", - "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[4]]", - "name": "[last(split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/'))]" - }, - "cMKUserAssignedIdentity": { - "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId')))]", - "existing": true, - "type": "Microsoft.ManagedIdentity/userAssignedIdentities", - "apiVersion": "2025-01-31-preview", - "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/')[2]]", - "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/')[4]]", - "name": "[last(split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/'))]" - }, - "cognitiveService": { - "type": "Microsoft.CognitiveServices/accounts", - "apiVersion": "2025-04-01-preview", - "name": "[parameters('name')]", - "kind": "[parameters('kind')]", - "identity": "[variables('identity')]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", - "sku": { - "name": "[parameters('sku')]" - }, - "properties": { - "allowProjectManagement": "[parameters('allowProjectManagement')]", - "customSubDomainName": "[parameters('customSubDomainName')]", - "networkAcls": "[if(not(empty(coalesce(parameters('networkAcls'), createObject()))), createObject('defaultAction', tryGet(parameters('networkAcls'), 'defaultAction'), 'virtualNetworkRules', coalesce(tryGet(parameters('networkAcls'), 'virtualNetworkRules'), createArray()), 'ipRules', coalesce(tryGet(parameters('networkAcls'), 'ipRules'), createArray())), null())]", - "publicNetworkAccess": "[if(not(equals(parameters('publicNetworkAccess'), null())), parameters('publicNetworkAccess'), if(not(empty(parameters('networkAcls'))), 'Enabled', 'Disabled'))]", - "allowedFqdnList": "[parameters('allowedFqdnList')]", - "apiProperties": "[parameters('apiProperties')]", - "disableLocalAuth": "[parameters('disableLocalAuth')]", - "encryption": "[if(not(empty(parameters('customerManagedKey'))), createObject('keySource', 'Microsoft.KeyVault', 'keyVaultProperties', createObject('identityClientId', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), ''))), reference('cMKUserAssignedIdentity').clientId, null()), 'keyVaultUri', reference('cMKKeyVault').vaultUri, 'keyName', parameters('customerManagedKey').keyName, 'keyVersion', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'keyVersion'), ''))), tryGet(parameters('customerManagedKey'), 'keyVersion'), last(split(reference('cMKKeyVault::cMKKey').keyUriWithVersion, '/'))))), null())]", - "migrationToken": "[parameters('migrationToken')]", - "restore": "[parameters('restore')]", - "restrictOutboundNetworkAccess": "[parameters('restrictOutboundNetworkAccess')]", - "userOwnedStorage": "[parameters('userOwnedStorage')]", - "dynamicThrottlingEnabled": "[parameters('dynamicThrottlingEnabled')]" - }, - "dependsOn": [ - "cMKKeyVault", - "cMKKeyVault::cMKKey", - "cMKUserAssignedIdentity" - ] - }, - "cognitiveService_deployments": { - "copy": { - "name": "cognitiveService_deployments", - "count": "[length(coalesce(parameters('deployments'), createArray()))]", - "mode": "serial", - "batchSize": 1 - }, - "type": "Microsoft.CognitiveServices/accounts/deployments", - "apiVersion": "2025-04-01-preview", - "name": "[format('{0}/{1}', parameters('name'), coalesce(tryGet(coalesce(parameters('deployments'), createArray())[copyIndex()], 'name'), format('{0}-deployments', parameters('name'))))]", - "properties": { - "model": "[coalesce(parameters('deployments'), createArray())[copyIndex()].model]", - "raiPolicyName": "[tryGet(coalesce(parameters('deployments'), createArray())[copyIndex()], 'raiPolicyName')]", - "versionUpgradeOption": "[tryGet(coalesce(parameters('deployments'), createArray())[copyIndex()], 'versionUpgradeOption')]" - }, - "sku": "[coalesce(tryGet(coalesce(parameters('deployments'), createArray())[copyIndex()], 'sku'), createObject('name', parameters('sku'), 'capacity', tryGet(parameters('sku'), 'capacity'), 'tier', tryGet(parameters('sku'), 'tier'), 'size', tryGet(parameters('sku'), 'size'), 'family', tryGet(parameters('sku'), 'family')))]", - "dependsOn": [ - "cognitiveService" - ] - }, - "cognitiveService_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.CognitiveServices/accounts/{0}', parameters('name'))]", - "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", - "properties": { - "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", - "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" - }, - "dependsOn": [ - "cognitiveService" - ] - }, - "cognitiveService_diagnosticSettings": { - "copy": { - "name": "cognitiveService_diagnosticSettings", - "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" - }, - "type": "Microsoft.Insights/diagnosticSettings", - "apiVersion": "2021-05-01-preview", - "scope": "[format('Microsoft.CognitiveServices/accounts/{0}', parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", - "properties": { - "copy": [ - { - "name": "metrics", - "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", - "input": { - "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", - "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", - "timeGrain": null - } - }, - { - "name": "logs", - "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", - "input": { - "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", - "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", - "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" - } - } - ], - "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", - "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", - "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", - "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", - "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", - "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" - }, - "dependsOn": [ - "cognitiveService" - ] - }, - "cognitiveService_roleAssignments": { - "copy": { - "name": "cognitiveService_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.CognitiveServices/accounts/{0}', parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.CognitiveServices/accounts', 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": [ - "cognitiveService" - ] - }, - "cognitiveService_privateEndpoints": { - "copy": { - "name": "cognitiveService_privateEndpoints", - "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-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]]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'name'), format('pep-{0}-{1}-{2}', last(split(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'account'), copyIndex()))]" - }, - "privateLinkServiceConnections": "[if(not(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true())), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'account'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'account')))))), createObject('value', null()))]", - "manualPrivateLinkServiceConnections": "[if(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true()), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'account'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'account')), 'requestMessage', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualConnectionRequestMessage'), 'Manual approval required.'))))), createObject('value', null()))]", - "subnetResourceId": { - "value": "[coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId]" - }, - "enableTelemetry": { - "value": "[variables('enableReferencedModulesTelemetry')]" - }, - "location": { - "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'location'), reference(split(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location)]" - }, - "lock": { - "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" - }, - "privateDnsZoneGroup": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroup')]" - }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'roleAssignments')]" - }, - "tags": { - "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" - }, - "customDnsConfigs": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customDnsConfigs')]" - }, - "ipConfigurations": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'ipConfigurations')]" - }, - "applicationSecurityGroupResourceIds": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'applicationSecurityGroupResourceIds')]" - }, - "customNetworkInterfaceName": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customNetworkInterfaceName')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "languageVersion": "2.0", - "contentVersion": "1.0.0.0", + "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.34.44.8038", - "templateHash": "12389807800450456797" + "version": "0.33.13.18514", + "templateHash": "15193761941438215308" }, - "name": "Private Endpoints", - "description": "This module deploys a Private Endpoint." + "name": "Storage Account File Shares", + "description": "This module deploys a Storage Account File Share." }, "definitions": { - "privateDnsZoneGroupType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the Private DNS Zone Group." - } - }, - "privateDnsZoneGroupConfigs": { - "type": "array", - "items": { - "$ref": "#/definitions/privateDnsZoneGroupConfigType" - }, - "metadata": { - "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." - } - } - }, - "metadata": { - "__bicep_export!": true - } - }, - "ipConfigurationType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the resource that is unique within a resource group." - } - }, - "properties": { - "type": "object", - "properties": { - "groupId": { - "type": "string", - "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." - } - }, - "memberName": { - "type": "string", - "metadata": { - "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." - } - }, - "privateIPAddress": { - "type": "string", - "metadata": { - "description": "Required. A private IP address obtained from the private endpoint's subnet." - } - } - }, - "metadata": { - "description": "Required. Properties of private endpoint IP configurations." - } - } - }, - "metadata": { - "__bicep_export!": true - } - }, - "privateLinkServiceConnectionType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the private link service connection." - } - }, - "properties": { - "type": "object", - "properties": { - "groupIds": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." - } - }, - "privateLinkServiceId": { - "type": "string", - "metadata": { - "description": "Required. The resource id of private link service." - } - }, - "requestMessage": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." - } - } - }, - "metadata": { - "description": "Required. Properties of private link service connection." - } - } - }, - "metadata": { - "__bicep_export!": true - } - }, - "customDnsConfigType": { - "type": "object", - "properties": { - "fqdn": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. FQDN that resolves to private endpoint IP address." - } - }, - "ipAddresses": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. A list of private IP addresses of the private endpoint." - } - } - }, - "metadata": { - "__bicep_export!": true - } - }, - "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." - } - } - }, - "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.5.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": { - "__bicep_imported_from!": { - "sourceTemplate": "private-dns-zone-group/main.bicep" - } - } - }, "roleAssignmentType": { "type": "object", "properties": { @@ -44015,64 +33582,67 @@ } }, "parameters": { - "name": { + "storageAccountName": { "type": "string", + "maxLength": 24, "metadata": { - "description": "Required. Name of the private endpoint resource to create." + "description": "Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment." } }, - "subnetResourceId": { + "fileServicesName": { "type": "string", + "defaultValue": "default", "metadata": { - "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + "description": "Conditional. The name of the parent file service. Required if the template is used in a standalone deployment." } }, - "applicationSecurityGroupResourceIds": { - "type": "array", - "items": { - "type": "string" - }, - "nullable": true, + "name": { + "type": "string", "metadata": { - "description": "Optional. Application security groups in which the private endpoint IP configuration is included." + "description": "Required. The name of the file share to create." } }, - "customNetworkInterfaceName": { + "accessTier": { "type": "string", - "nullable": true, + "defaultValue": "TransactionOptimized", + "allowedValues": [ + "Premium", + "Hot", + "Cool", + "TransactionOptimized" + ], "metadata": { - "description": "Optional. The custom name of the network interface attached to the private endpoint." + "description": "Conditional. Access tier for specific share. Required if the Storage Account kind is set to FileStorage (should be set to \"Premium\"). GpV2 account can choose between TransactionOptimized (default), Hot, and Cool." } }, - "ipConfigurations": { - "type": "array", - "items": { - "$ref": "#/definitions/ipConfigurationType" - }, - "nullable": true, + "shareQuota": { + "type": "int", + "defaultValue": 5120, "metadata": { - "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." + "description": "Optional. The maximum size of the share, in gigabytes. Must be greater than 0, and less than or equal to 5120 (5TB). For Large File Shares, the maximum size is 102400 (100TB)." } }, - "privateDnsZoneGroup": { - "$ref": "#/definitions/privateDnsZoneGroupType", - "nullable": true, - "metadata": { - "description": "Optional. The private DNS zone group to configure for the private endpoint." - } - }, - "location": { + "enabledProtocols": { "type": "string", - "defaultValue": "[resourceGroup().location]", + "defaultValue": "SMB", + "allowedValues": [ + "NFS", + "SMB" + ], "metadata": { - "description": "Optional. Location for all Resources." + "description": "Optional. The authentication protocol that is used for the file share. Can only be specified when creating a share." } }, - "lock": { - "$ref": "#/definitions/lockType", - "nullable": true, + "rootSquash": { + "type": "string", + "defaultValue": "NoRootSquash", + "allowedValues": [ + "AllSquash", + "NoRootSquash", + "RootSquash" + ], "metadata": { - "description": "Optional. The lock settings of the service." + "description": "Optional. Permissions for NFS file shares are enforced by the client OS rather than the Azure Files service. Toggling the root squash behavior reduces the rights of the root user for NFS shares." } }, "roleAssignments": { @@ -44084,50 +33654,6 @@ "metadata": { "description": "Optional. Array of role assignments to create." } - }, - "tags": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." - } - }, - "customDnsConfigs": { - "type": "array", - "items": { - "$ref": "#/definitions/customDnsConfigType" - }, - "nullable": true, - "metadata": { - "description": "Optional. Custom DNS configurations." - } - }, - "manualPrivateLinkServiceConnections": { - "type": "array", - "items": { - "$ref": "#/definitions/privateLinkServiceConnectionType" - }, - "nullable": true, - "metadata": { - "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." - } - }, - "privateLinkServiceConnections": { - "type": "array", - "items": { - "$ref": "#/definitions/privateLinkServiceConnectionType" - }, - "nullable": true, - "metadata": { - "description": "Conditional. A grouping of information about the connection to the remote resource. Required if `manualPrivateLinkServiceConnections` is empty." - } - }, - "enableTelemetry": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Optional. Enable/Disable usage telemetry for module." - } } }, "variables": { @@ -44140,325 +33666,583 @@ ], "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')]" + "Reader and Data Access": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c12c1c16-33a1-487b-954d-41c89c60f349')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Storage Account Backup Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e5e2a7ff-d759-4cd2-bb51-3152d37e2eb1')]", + "Storage Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab')]", + "Storage Account Key Operator Service Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '81a9662b-bebf-436f-a333-f67b29880f12')]", + "Storage File Data SMB Share Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0c867c2a-1d8c-454a-a3db-ab2ea1bdc8bb')]", + "Storage File Data SMB Share Elevated Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a7264617-510b-434b-a828-9731dc254ea7')]", + "Storage File Data SMB Share Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aba4ae5f-2193-4029-9191-0cb91df5e314')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, "resources": { - "avmTelemetry": { - "condition": "[parameters('enableTelemetry')]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2024-03-01", - "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.11.0', '.', '-'), 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" - } - } - } - } + "storageAccount::fileService": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts/fileServices", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}', parameters('storageAccountName'), parameters('fileServicesName'))]" }, - "privateEndpoint": { - "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2024-05-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')]" - } - } + "storageAccount": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2023-04-01", + "name": "[parameters('storageAccountName')]" }, - "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')))]", + "fileShare": { + "type": "Microsoft.Storage/storageAccounts/fileServices/shares", + "apiVersion": "2023-01-01", + "name": "[format('{0}/{1}/{2}', parameters('storageAccountName'), parameters('fileServicesName'), parameters('name'))]", "properties": { - "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", - "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" - ] + "accessTier": "[parameters('accessTier')]", + "shareQuota": "[parameters('shareQuota')]", + "rootSquash": "[if(equals(parameters('enabledProtocols'), 'NFS'), parameters('rootSquash'), null())]", + "enabledProtocols": "[parameters('enabledProtocols')]" + } }, - "privateEndpoint_roleAssignments": { + "fileShare_roleAssignments": { "copy": { - "name": "privateEndpoint_roleAssignments", + "name": "fileShare_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": "2022-09-01", - "name": "[format('{0}-PrivateEndpoint-PrivateDnsZoneGroup', uniqueString(deployment().name))]", + "name": "[format('{0}-Share-Rbac-{1}', uniqueString(deployment().name), copyIndex())]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { + "scope": { + "value": "[replace(resourceId('Microsoft.Storage/storageAccounts/fileServices/shares', parameters('storageAccountName'), parameters('fileServicesName'), parameters('name')), '/shares/', '/fileshares/')]" + }, "name": { - "value": "[tryGet(parameters('privateDnsZoneGroup'), 'name')]" + "value": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Storage/storageAccounts/fileServices/shares', parameters('storageAccountName'), parameters('fileServicesName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]" }, - "privateEndpointName": { - "value": "[parameters('name')]" + "roleDefinitionId": { + "value": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]" }, - "privateDnsZoneConfigs": { - "value": "[parameters('privateDnsZoneGroup').privateDnsZoneGroupConfigs]" + "principalId": { + "value": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]" + }, + "principalType": { + "value": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]" + }, + "condition": { + "value": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]" + }, + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), createObject('value', coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0')), createObject('value', null()))]", + "delegatedManagedIdentityResourceId": { + "value": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "description": { + "value": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]" } }, "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.34.44.8038", - "templateHash": "13997305779829540948" - }, - "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." - } - } - }, + "parameters": { + "scope": { + "type": "string", "metadata": { - "__bicep_export!": true + "description": "Required. The scope to deploy the role assignment to." } - } - }, - "parameters": { - "privateEndpointName": { + }, + "name": { "type": "string", "metadata": { - "description": "Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment." + "description": "Required. The name of the role assignment." } }, - "privateDnsZoneConfigs": { - "type": "array", - "items": { - "$ref": "#/definitions/privateDnsZoneGroupConfigType" - }, - "minLength": 1, - "maxLength": 5, + "roleDefinitionId": { + "type": "string", "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." + "description": "Required. The role definition Id to assign." } }, - "name": { + "principalId": { "type": "string", - "defaultValue": "default", "metadata": { - "description": "Optional. The name of the private DNS zone group." + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." } - } - }, - "variables": { - "copy": [ - { - "name": "privateDnsZoneConfigsVar", - "count": "[length(parameters('privateDnsZoneConfigs'))]", - "input": { - "name": "[coalesce(tryGet(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')], 'name'), last(split(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId, '/')))]", - "properties": { - "privateDnsZoneId": "[parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId]" - } - } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User", + "" + ], + "defaultValue": "", + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." } - ] - }, - "resources": { - "privateEndpoint": { - "existing": true, - "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2024-05-01", - "name": "[parameters('privateEndpointName')]" }, - "privateDnsZoneGroup": { - "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2024-05-01", - "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", - "properties": { - "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" + "description": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The description of the role assignment." } - } - }, - "outputs": { - "name": { + }, + "condition": { "type": "string", + "defaultValue": "", "metadata": { - "description": "The name of the private endpoint DNS zone group." - }, - "value": "[parameters('name')]" + "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\"" + } }, - "resourceId": { + "conditionVersion": { "type": "string", + "allowedValues": [ + "2.0" + ], + "defaultValue": "2.0", "metadata": { - "description": "The resource ID of the private endpoint DNS zone group." - }, - "value": "[resourceId('Microsoft.Network/privateEndpoints/privateDnsZoneGroups', parameters('privateEndpointName'), parameters('name'))]" + "description": "Optional. Version of the condition." + } }, - "resourceGroupName": { + "delegatedManagedIdentityResourceId": { "type": "string", + "defaultValue": "", "metadata": { - "description": "The resource group the private endpoint DNS zone group was deployed into." - }, - "value": "[resourceGroup().name]" + "description": "Optional. The Resource Id of the delegated managed identity resource." + } } - } + }, + "resources": [ + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[parameters('scope')]", + "name": "[parameters('name')]", + "properties": { + "roleDefinitionId": "[parameters('roleDefinitionId')]", + "principalId": "[parameters('principalId')]", + "description": "[parameters('description')]", + "principalType": "[if(not(empty(parameters('principalType'))), parameters('principalType'), null())]", + "condition": "[if(not(empty(parameters('condition'))), parameters('condition'), null())]", + "conditionVersion": "[if(and(not(empty(parameters('conditionVersion'))), not(empty(parameters('condition')))), parameters('conditionVersion'), null())]", + "delegatedManagedIdentityResourceId": "[if(not(empty(parameters('delegatedManagedIdentityResourceId'))), parameters('delegatedManagedIdentityResourceId'), null())]" + } + } + ] } }, "dependsOn": [ - "privateEndpoint" + "fileShare" ] } }, "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." + "description": "The name of the deployed file share." }, "value": "[parameters('name')]" }, - "location": { + "resourceId": { "type": "string", "metadata": { - "description": "The location the resource was deployed into." - }, - "value": "[reference('privateEndpoint', '2024-05-01', 'full').location]" - }, - "customDnsConfigs": { - "type": "array", - "items": { - "$ref": "#/definitions/customDnsConfigType" - }, - "metadata": { - "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." + "description": "The resource ID of the deployed file share." }, - "value": "[map(reference('privateEndpoint').networkInterfaces, lambda('nic', lambdaVariables('nic').id))]" + "value": "[resourceId('Microsoft.Storage/storageAccounts/fileServices/shares', parameters('storageAccountName'), parameters('fileServicesName'), parameters('name'))]" }, - "groupId": { + "resourceGroupName": { "type": "string", - "nullable": true, "metadata": { - "description": "The group Id for the private endpoint Group." + "description": "The resource group of the deployed file share." }, - "value": "[coalesce(tryGet(tryGet(tryGet(tryGet(reference('privateEndpoint'), 'manualPrivateLinkServiceConnections'), 0, 'properties'), 'groupIds'), 0), tryGet(tryGet(tryGet(tryGet(reference('privateEndpoint'), 'privateLinkServiceConnections'), 0, 'properties'), 'groupIds'), 0))]" + "value": "[resourceGroup().name]" } } } }, "dependsOn": [ - "cognitiveService" + "fileServices", + "storageAccount" ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed file share service." + }, + "value": "[parameters('name')]" }, - "secretsExport": { - "condition": "[not(equals(parameters('secretsExportConfiguration'), null()))]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-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]]", + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed file share service." + }, + "value": "[resourceId('Microsoft.Storage/storageAccounts/fileServices', parameters('storageAccountName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed file share service." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "storageAccount" + ] + }, + "storageAccount_queueServices": { + "condition": "[not(empty(parameters('queueServices')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Storage-QueueServices', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "storageAccountName": { + "value": "[parameters('name')]" + }, + "diagnosticSettings": { + "value": "[tryGet(parameters('queueServices'), 'diagnosticSettings')]" + }, + "queues": { + "value": "[tryGet(parameters('queueServices'), 'queues')]" + }, + "corsRules": { + "value": "[tryGet(parameters('queueServices'), 'corsRules')]" + } + }, + "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.33.13.18514", + "templateHash": "8158577333548255612" + }, + "name": "Storage Account Queue Services", + "description": "This module deploys a Storage Account Queue Service." + }, + "definitions": { + "corsRuleType": { + "type": "object", "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "keyVaultName": { - "value": "[last(split(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '/'))]" + "allowedHeaders": { + "type": "array", + "items": { + "type": "string" }, - "secretsToSet": { - "value": "[union(createArray(), if(contains(parameters('secretsExportConfiguration'), 'accessKey1Name'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'accessKey1Name'), 'value', listKeys('cognitiveService', '2025-04-01-preview').key1)), createArray()), if(contains(parameters('secretsExportConfiguration'), 'accessKey2Name'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'accessKey2Name'), 'value', listKeys('cognitiveService', '2025-04-01-preview').key2)), createArray()))]" + "metadata": { + "description": "Required. A list of headers allowed to be part of the cross-origin request." + } + }, + "allowedMethods": { + "type": "array", + "allowedValues": [ + "CONNECT", + "DELETE", + "GET", + "HEAD", + "MERGE", + "OPTIONS", + "PATCH", + "POST", + "PUT", + "TRACE" + ], + "metadata": { + "description": "Required. A list of HTTP methods that are allowed to be executed by the origin." + } + }, + "allowedOrigins": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of origin domains that will be allowed via CORS, or \"*\" to allow all domains." + } + }, + "exposedHeaders": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of response headers to expose to CORS clients." + } + }, + "maxAgeInSeconds": { + "type": "int", + "metadata": { + "description": "Required. The number of seconds that the client/browser should cache a preflight response." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for a cors rule." + } + }, + "diagnosticSettingFullType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "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 logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "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 both logs & metrics are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + } + }, + "parameters": { + "storageAccountName": { + "type": "string", + "maxLength": 24, + "metadata": { + "description": "Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment." + } + }, + "queues": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. Queues to create." + } + }, + "corsRules": { + "type": "array", + "items": { + "$ref": "#/definitions/corsRuleType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The List of CORS rules. You can include up to five CorsRule elements in the request." + } + }, + "diagnosticSettings": { + "type": "array", + "items": { + "$ref": "#/definitions/diagnosticSettingFullType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + } + }, + "variables": { + "name": "default" + }, + "resources": { + "storageAccount": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2023-04-01", + "name": "[parameters('storageAccountName')]" + }, + "queueServices": { + "type": "Microsoft.Storage/storageAccounts/queueServices", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}', parameters('storageAccountName'), variables('name'))]", + "properties": { + "cors": "[if(not(equals(parameters('corsRules'), null())), createObject('corsRules', parameters('corsRules')), null())]" + } + }, + "queueServices_diagnosticSettings": { + "copy": { + "name": "queueServices_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}/queueServices/{1}', parameters('storageAccountName'), variables('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', variables('name')))]", + "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null + } + }, + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "queueServices" + ] + }, + "queueServices_queues": { + "copy": { + "name": "queueServices_queues", + "count": "[length(coalesce(parameters('queues'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Queue-{1}', deployment().name, copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "storageAccountName": { + "value": "[parameters('storageAccountName')]" + }, + "name": { + "value": "[coalesce(parameters('queues'), createArray())[copyIndex()].name]" + }, + "metadata": { + "value": "[tryGet(coalesce(parameters('queues'), createArray())[copyIndex()], 'metadata')]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('queues'), createArray())[copyIndex()], 'roleAssignments')]" } }, "template": { @@ -44468,58 +34252,83 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "1200612323329026557" - } + "version": "0.33.13.18514", + "templateHash": "9877120144610775153" + }, + "name": "Storage Account Queues", + "description": "This module deploys a Storage Account Queue." }, "definitions": { - "secretSetOutputType": { + "roleAssignmentType": { "type": "object", "properties": { - "secretResourceId": { + "name": { "type": "string", + "nullable": true, "metadata": { - "description": "The resourceId of the exported secret." + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." } }, - "secretUri": { + "roleDefinitionIdOrName": { "type": "string", "metadata": { - "description": "The secret URI of the exported secret." + "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'." } }, - "secretUriWithVersion": { + "principalId": { "type": "string", "metadata": { - "description": "The secret URI with version of the exported secret." + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." } - } - }, - "metadata": { - "description": "An AVM-aligned type for the output of the secret set via the secrets export feature.", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } - } - }, - "secretToSetType": { - "type": "object", - "properties": { - "name": { + }, + "principalType": { "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, "metadata": { - "description": "Required. The name of the secret to set." + "description": "Optional. The principal type of the assigned principal ID." } }, - "value": { - "type": "securestring", + "description": { + "type": "string", + "nullable": true, "metadata": { - "description": "Required. The value of the secret to set." + "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 the secret to set via the secrets export feature.", + "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.5.1" } @@ -44527,963 +34336,768 @@ } }, "parameters": { - "keyVaultName": { + "storageAccountName": { "type": "string", + "maxLength": 24, "metadata": { - "description": "Required. The name of the Key Vault to set the ecrets in." + "description": "Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment." } }, - "secretsToSet": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the storage queue to deploy." + } + }, + "metadata": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. A name-value pair that represents queue metadata." + } + }, + "roleAssignments": { "type": "array", "items": { - "$ref": "#/definitions/secretToSetType" + "$ref": "#/definitions/roleAssignmentType" }, + "nullable": true, "metadata": { - "description": "Required. The secrets to set in the Key Vault." + "description": "Optional. Array of role assignments to create." + } + } + }, + "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')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Reader and Data Access": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c12c1c16-33a1-487b-954d-41c89c60f349')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Storage Account Backup Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e5e2a7ff-d759-4cd2-bb51-3152d37e2eb1')]", + "Storage Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab')]", + "Storage Account Key Operator Service Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '81a9662b-bebf-436f-a333-f67b29880f12')]", + "Storage Queue Data Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '974c5e8b-45b9-4653-ba55-5f855dd0fb88')]", + "Storage Queue Data Message Processor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8a0f0c08-91a1-4084-bc3d-661d67233fed')]", + "Storage Queue Data Message Sender": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c6a89b2d-59bc-44d0-9896-0f6e12d7b80a')]", + "Storage Queue Data Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '19e7f393-937e-4f77-808e-94535e297925')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, "resources": { - "keyVault": { + "storageAccount::queueServices": { "existing": true, - "type": "Microsoft.KeyVault/vaults", - "apiVersion": "2023-07-01", - "name": "[parameters('keyVaultName')]" + "type": "Microsoft.Storage/storageAccounts/queueServices", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}', parameters('storageAccountName'), 'default')]" }, - "secrets": { + "storageAccount": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2023-04-01", + "name": "[parameters('storageAccountName')]" + }, + "queue": { + "type": "Microsoft.Storage/storageAccounts/queueServices/queues", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}/{2}', parameters('storageAccountName'), 'default', parameters('name'))]", + "properties": { + "metadata": "[parameters('metadata')]" + } + }, + "queue_roleAssignments": { "copy": { - "name": "secrets", - "count": "[length(parameters('secretsToSet'))]" + "name": "queue_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, - "type": "Microsoft.KeyVault/vaults/secrets", - "apiVersion": "2023-07-01", - "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('secretsToSet')[copyIndex()].name)]", + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}/queueServices/{1}/queues/{2}', parameters('storageAccountName'), 'default', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Storage/storageAccounts/queueServices/queues', parameters('storageAccountName'), 'default', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "value": "[parameters('secretsToSet')[copyIndex()].value]" - } + "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": [ + "queue" + ] } }, "outputs": { - "secretsSet": { - "type": "array", - "items": { - "$ref": "#/definitions/secretSetOutputType" + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed queue." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed queue." }, + "value": "[resourceId('Microsoft.Storage/storageAccounts/queueServices/queues', parameters('storageAccountName'), 'default', parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", "metadata": { - "description": "The references to the secrets exported to the provided Key Vault." + "description": "The resource group of the deployed queue." }, - "copy": { - "count": "[length(range(0, length(coalesce(parameters('secretsToSet'), createArray()))))]", - "input": { - "secretResourceId": "[resourceId('Microsoft.KeyVault/vaults/secrets', parameters('keyVaultName'), parameters('secretsToSet')[range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()]].name)]", - "secretUri": "[reference(format('secrets[{0}]', range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()])).secretUri]", - "secretUriWithVersion": "[reference(format('secrets[{0}]', range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()])).secretUriWithVersion]" - } - } + "value": "[resourceGroup().name]" } } } - }, - "dependsOn": [ - "cognitiveService" - ] + } } }, "outputs": { "name": { "type": "string", "metadata": { - "description": "The name of the cognitive services account." + "description": "The name of the deployed file share service." }, - "value": "[parameters('name')]" + "value": "[variables('name')]" }, "resourceId": { "type": "string", "metadata": { - "description": "The resource ID of the cognitive services account." + "description": "The resource ID of the deployed file share service." }, - "value": "[resourceId('Microsoft.CognitiveServices/accounts', parameters('name'))]" + "value": "[resourceId('Microsoft.Storage/storageAccounts/queueServices', parameters('storageAccountName'), variables('name'))]" }, "resourceGroupName": { "type": "string", "metadata": { - "description": "The resource group the cognitive services account was deployed into." + "description": "The resource group of the deployed file share service." }, "value": "[resourceGroup().name]" - }, - "endpoint": { - "type": "string", - "metadata": { - "description": "The service endpoint of the cognitive services account." - }, - "value": "[reference('cognitiveService').endpoint]" - }, - "endpoints": { - "$ref": "#/definitions/endpointType", - "metadata": { - "description": "All endpoints available for the cognitive services account, types depends on the cognitive service kind." - }, - "value": "[reference('cognitiveService').endpoints]" - }, - "systemAssignedMIPrincipalId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "The principal ID of the system assigned identity." - }, - "value": "[tryGet(tryGet(reference('cognitiveService', '2025-04-01-preview', 'full'), 'identity'), 'principalId')]" - }, - "location": { - "type": "string", - "metadata": { - "description": "The location the resource was deployed into." - }, - "value": "[reference('cognitiveService', '2025-04-01-preview', 'full').location]" - }, - "exportedSecrets": { - "$ref": "#/definitions/secretsOutputType", - "metadata": { - "description": "A hashtable of references to the secrets exported to the provided Key Vault. The key of each reference is each secret's name." - }, - "value": "[if(not(equals(parameters('secretsExportConfiguration'), null())), toObject(reference('secretsExport').outputs.secretsSet.value, lambda('secret', last(split(lambdaVariables('secret').secretResourceId, '/'))), lambda('secret', lambdaVariables('secret'))), createObject())]" - }, - "privateEndpoints": { - "type": "array", - "items": { - "$ref": "#/definitions/privateEndpointOutputType" - }, - "metadata": { - "description": "The private endpoints of the congitive services account." - }, - "copy": { - "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]", - "input": { - "name": "[reference(format('cognitiveService_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", - "resourceId": "[reference(format('cognitiveService_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", - "groupId": "[tryGet(tryGet(reference(format('cognitiveService_privateEndpoints[{0}]', copyIndex())).outputs, 'groupId'), 'value')]", - "customDnsConfigs": "[reference(format('cognitiveService_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfigs.value]", - "networkInterfaceResourceIds": "[reference(format('cognitiveService_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceResourceIds.value]" - } - } } } } - } - } - }, - "outputs": { - "resourceId": { - "type": "string", - "value": "[reference('cognitiveService').outputs.resourceId.value]" - }, - "name": { - "type": "string", - "value": "[reference('cognitiveService').outputs.name.value]" - }, - "systemAssignedMIPrincipalId": { - "type": "string", - "nullable": true, - "value": "[tryGet(tryGet(reference('cognitiveService').outputs, 'systemAssignedMIPrincipalId'), 'value')]" - }, - "endpoint": { - "type": "string", - "value": "[reference('cognitiveService').outputs.endpoint.value]" + }, + "dependsOn": [ + "storageAccount" + ] }, - "foundryConnection": { - "type": "object", - "value": { - "name": "[reference('cognitiveService').outputs.name.value]", - "value": null, - "category": "[parameters('category')]", - "target": "[reference('cognitiveService').outputs.endpoint.value]", - "kind": "[parameters('kind')]", - "connectionProperties": { - "authType": "AAD" - }, - "isSharedToAll": true, - "metadata": { - "ApiType": "Azure", - "Kind": "[parameters('kind')]", - "ResourceId": "[reference('cognitiveService').outputs.resourceId.value]" - } - } - } - } - } - }, - "dependsOn": [ - "cognitiveServicesPrivateDnsZone" - ] - }, - "language": { - "condition": "[parameters('languageEnabled')]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[take(format('{0}-language-deployment', parameters('name')), 64)]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[format('lang{0}{1}', parameters('name'), parameters('resourceToken'))]" - }, - "location": { - "value": "[parameters('location')]" - }, - "kind": { - "value": "TextAnalytics" - }, - "sku": { - "value": "S" - }, - "networkIsolation": { - "value": "[parameters('networkIsolation')]" - }, - "networkAcls": { - "value": "[parameters('networkAcls')]" - }, - "virtualNetworkSubnetResourceId": "[if(parameters('networkIsolation'), createObject('value', parameters('virtualNetworkSubnetResourceId')), createObject('value', ''))]", - "privateDnsZonesResourceIds": "[if(parameters('networkIsolation'), createObject('value', createArray(reference('cognitiveServicesPrivateDnsZone').outputs.resourceId.value)), createObject('value', createArray()))]", - "logAnalyticsWorkspaceResourceId": { - "value": "[parameters('logAnalyticsWorkspaceResourceId')]" - }, - "tags": { - "value": "[parameters('tags')]" - } - }, - "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.1.42791", - "templateHash": "2348080591288311162" - } - }, - "definitions": { - "deploymentsType": { - "type": "object", + "storageAccount_tableServices": { + "condition": "[not(empty(parameters('tableServices')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Storage-TableServices', uniqueString(deployment().name, parameters('location')))]", "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Specify the name of cognitive service account deployment." + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "storageAccountName": { + "value": "[parameters('name')]" + }, + "diagnosticSettings": { + "value": "[tryGet(parameters('tableServices'), 'diagnosticSettings')]" + }, + "tables": { + "value": "[tryGet(parameters('tableServices'), 'tables')]" + }, + "corsRules": { + "value": "[tryGet(parameters('tableServices'), 'corsRules')]" } }, - "model": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of Cognitive Services account deployment model." - } + "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.33.13.18514", + "templateHash": "541986423744885003" }, - "format": { - "type": "string", + "name": "Storage Account Table Services", + "description": "This module deploys a Storage Account Table Service." + }, + "definitions": { + "corsRuleType": { + "type": "object", + "properties": { + "allowedHeaders": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of headers allowed to be part of the cross-origin request." + } + }, + "allowedMethods": { + "type": "array", + "allowedValues": [ + "CONNECT", + "DELETE", + "GET", + "HEAD", + "MERGE", + "OPTIONS", + "PATCH", + "POST", + "PUT", + "TRACE" + ], + "metadata": { + "description": "Required. A list of HTTP methods that are allowed to be executed by the origin." + } + }, + "allowedOrigins": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of origin domains that will be allowed via CORS, or \"*\" to allow all domains." + } + }, + "exposedHeaders": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of response headers to expose to CORS clients." + } + }, + "maxAgeInSeconds": { + "type": "int", + "metadata": { + "description": "Required. The number of seconds that the client/browser should cache a preflight response." + } + } + }, "metadata": { - "description": "Required. The format of Cognitive Services account deployment model." + "__bicep_export!": true, + "description": "The type for a cors rule." } }, - "version": { - "type": "string", + "diagnosticSettingFullType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "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 logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "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": "Required. The version of Cognitive Services account deployment model." + "description": "An AVM-aligned type for a diagnostic setting. To be used if both logs & metrics are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } } } }, - "metadata": { - "description": "Required. Properties of Cognitive Services account deployment model." - } - }, - "sku": { - "type": "object", - "properties": { - "name": { + "parameters": { + "storageAccountName": { "type": "string", + "maxLength": 24, "metadata": { - "description": "Required. The name of the resource model definition representing SKU." - } - }, - "capacity": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The capacity of the resource model definition representing SKU." + "description": "Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment." } }, - "tier": { - "type": "string", - "nullable": true, + "tables": { + "type": "array", + "defaultValue": [], "metadata": { - "description": "Optional. The tier of the resource model definition representing SKU." + "description": "Optional. tables to create." } }, - "size": { - "type": "string", + "corsRules": { + "type": "array", + "items": { + "$ref": "#/definitions/corsRuleType" + }, "nullable": true, "metadata": { - "description": "Optional. The size of the resource model definition representing SKU." + "description": "Optional. The List of CORS rules. You can include up to five CorsRule elements in the request." } }, - "family": { - "type": "string", + "diagnosticSettings": { + "type": "array", + "items": { + "$ref": "#/definitions/diagnosticSettingFullType" + }, "nullable": true, "metadata": { - "description": "Optional. The family of the resource model definition representing SKU." + "description": "Optional. The diagnostic settings of the service." } } }, - "nullable": true, - "metadata": { - "description": "Optional. The resource model definition representing SKU." - } - }, - "raiPolicyName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of RAI policy." - } - }, - "versionUpgradeOption": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The version upgrade option." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "../customTypes.bicep" - } - } - }, - "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.5.1" - } - } - } - }, - "parameters": { - "name": { - "type": "string", - "metadata": { - "description": "Name of the Cognitive Services resource. Must be unique in the resource group." - } - }, - "location": { - "type": "string", - "metadata": { - "description": "The location of the Cognitive Services resource." - } - }, - "kind": { - "type": "string", - "allowedValues": [ - "AIServices", - "AnomalyDetector", - "CognitiveServices", - "ComputerVision", - "ContentModerator", - "ContentSafety", - "ConversationalLanguageUnderstanding", - "CustomVision.Prediction", - "CustomVision.Training", - "Face", - "FormRecognizer", - "HealthInsights", - "ImmersiveReader", - "Internal.AllInOne", - "LUIS", - "LUIS.Authoring", - "LanguageAuthoring", - "MetricsAdvisor", - "OpenAI", - "Personalizer", - "QnAMaker.v2", - "SpeechServices", - "TextAnalytics", - "TextTranslation" - ], - "metadata": { - "description": "Required. Kind of the Cognitive Services account. Use 'Get-AzCognitiveServicesAccountSku' to determine a valid combinations of 'kind' and 'SKU' for your Azure region." - } - }, - "sku": { - "type": "string", - "defaultValue": "S0", - "allowedValues": [ - "S", - "S0", - "S1", - "S2", - "S3", - "S4", - "S5", - "S6", - "S7", - "S8" - ], - "metadata": { - "description": "Required. The SKU of the Cognitive Services account. Use 'Get-AzCognitiveServicesAccountSku' to determine a valid combinations of 'kind' and 'SKU' for your Azure region." - } - }, - "category": { - "type": "string", - "defaultValue": "CognitiveService", - "metadata": { - "description": "Category of the Cognitive Services account." - } - }, - "networkIsolation": { - "type": "bool", - "metadata": { - "description": "Specifies whether to enable network isolation. If true, the resource will be deployed in a private endpoint and public network access will be disabled." - } - }, - "privateDnsZonesResourceIds": { - "type": "array", - "items": { - "type": "string" - }, - "defaultValue": [], - "metadata": { - "description": "Existing resource ID of the private DNS zone for the private endpoint." - } - }, - "virtualNetworkSubnetResourceId": { - "type": "string", - "metadata": { - "description": "Resource ID of the subnet for the private endpoint." - } - }, - "logAnalyticsWorkspaceResourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the Log Analytics workspace to use for diagnostic settings." - } - }, - "aiModelDeployments": { - "type": "array", - "items": { - "$ref": "#/definitions/deploymentsType" - }, - "defaultValue": [], - "metadata": { - "description": "Optional. Specifies the OpenAI deployments to create." - } - }, - "roleAssignments": { - "type": "array", - "items": { - "$ref": "#/definitions/roleAssignmentType" - }, - "nullable": true, - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "tags": { - "type": "object", - "defaultValue": {}, - "metadata": { - "description": "Optional. Tags to be applied to the resources." - } - }, - "networkAcls": { - "type": "object", - "metadata": { - "description": "Optional. A collection of rules governing the accessibility from specific network locations." - } - } - }, - "variables": { - "copy": [ - { - "name": "privateDnsZones", - "count": "[length(parameters('privateDnsZonesResourceIds'))]", - "input": { - "privateDnsZoneResourceId": "[parameters('privateDnsZonesResourceIds')[copyIndex('privateDnsZones')]]" - } - } - ], - "nameFormatted": "[take(toLower(parameters('name')), 24)]" - }, - "resources": { - "cognitiveService": { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[take(format('cog-{0}-{1}-deployment', parameters('kind'), parameters('name')), 64)]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[variables('nameFormatted')]" - }, - "location": { - "value": "[parameters('location')]" - }, - "tags": { - "value": "[parameters('tags')]" - }, - "sku": { - "value": "[parameters('sku')]" - }, - "kind": { - "value": "[parameters('kind')]" - }, - "allowProjectManagement": { - "value": true - }, - "managedIdentities": { - "value": { - "systemAssigned": true - } - }, - "deployments": { - "value": "[parameters('aiModelDeployments')]" - }, - "customSubDomainName": { - "value": "[parameters('name')]" - }, - "disableLocalAuth": { - "value": "[parameters('networkIsolation')]" + "variables": { + "name": "default" }, - "publicNetworkAccess": "[if(parameters('networkIsolation'), createObject('value', 'Disabled'), createObject('value', 'Enabled'))]", - "diagnosticSettings": { - "value": [ - { - "workspaceResourceId": "[parameters('logAnalyticsWorkspaceResourceId')]" + "resources": { + "storageAccount": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2023-04-01", + "name": "[parameters('storageAccountName')]" + }, + "tableServices": { + "type": "Microsoft.Storage/storageAccounts/tableServices", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}', parameters('storageAccountName'), variables('name'))]", + "properties": { + "cors": "[if(not(equals(parameters('corsRules'), null())), createObject('corsRules', parameters('corsRules')), null())]" } - ] - }, - "roleAssignments": { - "value": "[parameters('roleAssignments')]" - }, - "networkAcls": { - "value": "[parameters('networkAcls')]" - }, - "privateEndpoints": "[if(parameters('networkIsolation'), createObject('value', createArray(createObject('privateDnsZoneGroup', createObject('privateDnsZoneGroupConfigs', variables('privateDnsZones')), 'subnetResourceId', parameters('virtualNetworkSubnetResourceId')))), createObject('value', createArray()))]" - }, - "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.1.42791", - "templateHash": "16135659971302525380" }, - "name": "Cognitive Services", - "description": "This module deploys a Cognitive Service." - }, - "definitions": { - "privateEndpointOutputType": { - "type": "object", + "tableServices_diagnosticSettings": { + "copy": { + "name": "tableServices_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}/tableServices/{1}', parameters('storageAccountName'), variables('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', variables('name')))]", "properties": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the private endpoint." - } - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the private endpoint." - } - }, - "groupId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "The group Id for the private endpoint Group." - } - }, - "customDnsConfigs": { - "type": "array", - "items": { - "type": "object", - "properties": { - "fqdn": { - "type": "string", - "nullable": true, - "metadata": { - "description": "FQDN that resolves to private endpoint IP address." - } - }, - "ipAddresses": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "A list of private IP addresses of the private endpoint." - } - } + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null } }, - "metadata": { - "description": "The custom DNS configurations of the private endpoint." - } - }, - "networkInterfaceResourceIds": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "The IDs of the network interfaces associated with the private endpoint." + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } } - } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" }, - "metadata": { - "__bicep_export!": true, - "description": "The type for the private endpoint output." - } + "dependsOn": [ + "tableServices" + ] }, - "deploymentType": { - "type": "object", + "tableServices_tables": { + "copy": { + "name": "tableServices_tables", + "count": "[length(parameters('tables'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Table-{1}', deployment().name, copyIndex())]", "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Specify the name of cognitive service account deployment." + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('tables')[copyIndex()].name]" + }, + "storageAccountName": { + "value": "[parameters('storageAccountName')]" + }, + "roleAssignments": { + "value": "[tryGet(parameters('tables')[copyIndex()], 'roleAssignments')]" } }, - "model": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of Cognitive Services account deployment model." - } - }, - "format": { - "type": "string", - "metadata": { - "description": "Required. The format of Cognitive Services account deployment model." - } + "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.33.13.18514", + "templateHash": "11234204519679347949" }, - "version": { - "type": "string", + "name": "Storage Account Table", + "description": "This module deploys a Storage Account Table." + }, + "definitions": { + "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": "Required. The version of Cognitive Services account deployment model." + "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.5.1" + } } } }, - "metadata": { - "description": "Required. Properties of Cognitive Services account deployment model." - } - }, - "sku": { - "type": "object", - "properties": { - "name": { + "parameters": { + "storageAccountName": { "type": "string", + "maxLength": 24, "metadata": { - "description": "Required. The name of the resource model definition representing SKU." + "description": "Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment." } }, - "capacity": { - "type": "int", + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, "nullable": true, "metadata": { - "description": "Optional. The capacity of the resource model definition representing SKU." + "description": "Optional. Array of role assignments to create." } }, - "tier": { + "name": { "type": "string", - "nullable": true, "metadata": { - "description": "Optional. The tier of the resource model definition representing SKU." + "description": "Required. Name of the table." } - }, - "size": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The size of the resource model definition representing SKU." - } - }, - "family": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The family of the resource model definition representing SKU." + } + }, + "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')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Reader and Data Access": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c12c1c16-33a1-487b-954d-41c89c60f349')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Storage Account Backup Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e5e2a7ff-d759-4cd2-bb51-3152d37e2eb1')]", + "Storage Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab')]", + "Storage Account Key Operator Service Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '81a9662b-bebf-436f-a333-f67b29880f12')]", + "Storage Table Data Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3')]", + "Storage Table Data Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '76199698-9eea-4c19-bc75-cec21354c6b6')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, - "nullable": true, - "metadata": { - "description": "Optional. The resource model definition representing SKU." - } - }, - "raiPolicyName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of RAI policy." - } - }, - "versionUpgradeOption": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The version upgrade option." - } - } - }, - "metadata": { - "__bicep_export!": true, - "description": "The type for a cognitive services account deployment." - } - }, - "endpointType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Type of the endpoint." - } - }, - "endpoint": { - "type": "string", - "nullable": true, - "metadata": { - "description": "The endpoint URI." - } - } - }, - "metadata": { - "__bicep_export!": true, - "description": "The type for a cognitive services account endpoint." - } - }, - "secretsExportConfigurationType": { - "type": "object", - "properties": { - "keyVaultResourceId": { - "type": "string", - "metadata": { - "description": "Required. The key vault name where to store the keys and connection strings generated by the modules." - } - }, - "accessKey1Name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name for the accessKey1 secret to create." - } - }, - "accessKey2Name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name for the accessKey2 secret to create." - } - } - }, - "metadata": { - "__bicep_export!": true, - "description": "The type of the secrets exported to the provided Key Vault." - } - }, - "_1.privateEndpointCustomDnsConfigType": { - "type": "object", - "properties": { - "fqdn": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. FQDN that resolves to private endpoint IP address." - } - }, - "ipAddresses": { - "type": "array", - "items": { - "type": "string" + "resources": { + "storageAccount::tableServices": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts/tableServices", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}', parameters('storageAccountName'), 'default')]" + }, + "storageAccount": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2023-04-01", + "name": "[parameters('storageAccountName')]" + }, + "table": { + "type": "Microsoft.Storage/storageAccounts/tableServices/tables", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}/{2}', parameters('storageAccountName'), 'default', parameters('name'))]" + }, + "table_roleAssignments": { + "copy": { + "name": "table_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}/tableServices/{1}/tables/{2}', parameters('storageAccountName'), 'default', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Storage/storageAccounts/tableServices/tables', parameters('storageAccountName'), 'default', 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": [ + "table" + ] + } }, - "metadata": { - "description": "Required. A list of private IP addresses of the private endpoint." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } - } - }, - "_1.privateEndpointIpConfigurationType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the resource that is unique within a resource group." - } - }, - "properties": { - "type": "object", - "properties": { - "groupId": { + "outputs": { + "name": { "type": "string", "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." - } + "description": "The name of the deployed file share service." + }, + "value": "[parameters('name')]" }, - "memberName": { + "resourceId": { "type": "string", "metadata": { - "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to." - } + "description": "The resource ID of the deployed file share service." + }, + "value": "[resourceId('Microsoft.Storage/storageAccounts/tableServices/tables', parameters('storageAccountName'), 'default', parameters('name'))]" }, - "privateIPAddress": { + "resourceGroupName": { "type": "string", "metadata": { - "description": "Required. A private IP address obtained from the private endpoint's subnet." - } - } - }, - "metadata": { - "description": "Required. Properties of private endpoint IP configurations." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } - } - }, - "_1.privateEndpointPrivateDnsZoneGroupType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the Private DNS Zone Group." - } - }, - "privateDnsZoneGroupConfigs": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the private DNS Zone Group config." - } + "description": "The resource group of the deployed file share service." }, - "privateDnsZoneResourceId": { - "type": "string", - "metadata": { - "description": "Required. The resource id of the private DNS zone." - } - } + "value": "[resourceGroup().name]" } - }, - "metadata": { - "description": "Required. The private DNS Zone Groups to associate the Private Endpoint. A DNS Zone Group can support up to 5 DNS zones." } } + } + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed table service." }, + "value": "[variables('name')]" + }, + "resourceId": { + "type": "string", "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } - } + "description": "The resource ID of the deployed table service." + }, + "value": "[resourceId('Microsoft.Storage/storageAccounts/tableServices', parameters('storageAccountName'), variables('name'))]" }, - "_1.secretSetOutputType": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed table service." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "storageAccount" + ] + }, + "secretsExport": { + "condition": "[not(equals(parameters('secretsExportConfiguration'), null()))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-secrets-kv', uniqueString(deployment().name, parameters('location')))]", + "subscriptionId": "[split(coalesce(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '////'), '/')[4]]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "keyVaultName": { + "value": "[last(split(coalesce(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '//'), '/'))]" + }, + "secretsToSet": { + "value": "[union(createArray(), if(contains(parameters('secretsExportConfiguration'), 'accessKey1Name'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'accessKey1Name'), 'value', listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('name')), '2023-05-01').keys[0].value)), createArray()), if(contains(parameters('secretsExportConfiguration'), 'connectionString1Name'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'connectionString1Name'), 'value', format('DefaultEndpointsProtocol=https;AccountName={0};AccountKey={1};EndpointSuffix={2}', parameters('name'), listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('name')), '2023-05-01').keys[0].value, environment().suffixes.storage))), createArray()), if(contains(parameters('secretsExportConfiguration'), 'accessKey2Name'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'accessKey2Name'), 'value', listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('name')), '2023-05-01').keys[1].value)), createArray()), if(contains(parameters('secretsExportConfiguration'), 'connectionString2Name'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'connectionString2Name'), 'value', format('DefaultEndpointsProtocol=https;AccountName={0};AccountKey={1};EndpointSuffix={2}', parameters('name'), listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('name')), '2023-05-01').keys[1].value, environment().suffixes.storage))), createArray()))]" + } + }, + "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.33.13.18514", + "templateHash": "14510275109257916717" + } + }, + "definitions": { + "secretSetOutputType": { "type": "object", "properties": { "secretResourceId": { @@ -45512,1823 +35126,1582 @@ } } }, - "customerManagedKeyType": { + "secretToSetType": { "type": "object", "properties": { - "keyVaultResourceId": { - "type": "string", - "metadata": { - "description": "Required. The resource ID of a key vault to reference a customer managed key for encryption from." - } - }, - "keyName": { - "type": "string", - "metadata": { - "description": "Required. The name of the customer managed key to use for encryption." - } - }, - "keyVersion": { + "name": { "type": "string", - "nullable": true, "metadata": { - "description": "Optional. The version of the customer managed key to reference for encryption. If not provided, the deployment will use the latest version available at deployment time." + "description": "Required. The name of the secret to set." } }, - "userAssignedIdentityResourceId": { - "type": "string", - "nullable": true, + "value": { + "type": "securestring", "metadata": { - "description": "Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use." + "description": "Required. The value of the secret to set." } } }, "metadata": { - "description": "An AVM-aligned type for a customer-managed key. To be used if the resource type does not support auto-rotation of the customer-managed key.", + "description": "An AVM-aligned type for the secret to set via the secrets export feature.", "__bicep_imported_from!": { "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } - }, - "diagnosticSettingFullType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the diagnostic setting." - } - }, - "logCategoriesAndGroups": { - "type": "array", - "items": { - "type": "object", - "properties": { - "category": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." - } - }, - "categoryGroup": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." - } - }, - "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 logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." - } - }, - "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." - } - } - }, + } + }, + "parameters": { + "keyVaultName": { + "type": "string", "metadata": { - "description": "An AVM-aligned type for a diagnostic setting. To be used if both logs & metrics are supported by the resource provider.", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } + "description": "Required. The name of the Key Vault to set the ecrets in." } }, - "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." - } - } + "secretsToSet": { + "type": "array", + "items": { + "$ref": "#/definitions/secretToSetType" }, "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.5.1" - } + "description": "Required. The secrets to set in the Key Vault." } + } + }, + "resources": { + "keyVault": { + "existing": true, + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2022-07-01", + "name": "[parameters('keyVaultName')]" }, - "managedIdentityAllType": { - "type": "object", - "properties": { - "systemAssigned": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Enables system assigned managed identity on the resource." - } - }, - "userAssignedResourceIds": { - "type": "array", - "items": { - "type": "string" - }, - "nullable": true, - "metadata": { - "description": "Optional. The resource ID(s) to assign to the resource. Required if a user assigned identity is used for encryption." - } - } - }, - "metadata": { - "description": "An AVM-aligned type for a managed identity configuration. To be used if both a system-assigned & user-assigned identities are supported by the resource provider.", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } - } - }, - "privateEndpointSingleServiceType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the Private Endpoint." - } - }, - "location": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The location to deploy the Private Endpoint to." - } - }, - "privateLinkServiceConnectionName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the private link connection to create." - } - }, - "service": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The subresource to deploy the Private Endpoint for. For example \"vault\" for a Key Vault Private Endpoint." - } - }, - "subnetResourceId": { - "type": "string", - "metadata": { - "description": "Required. Resource ID of the subnet where the endpoint needs to be created." - } - }, - "resourceGroupResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The resource ID of the Resource Group the Private Endpoint will be created in. If not specified, the Resource Group of the provided Virtual Network Subnet is used." - } - }, - "privateDnsZoneGroup": { - "$ref": "#/definitions/_1.privateEndpointPrivateDnsZoneGroupType", - "nullable": true, - "metadata": { - "description": "Optional. The private DNS Zone Group to configure for the Private Endpoint." - } - }, - "isManualConnection": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. If Manual Private Link Connection is required." - } - }, - "manualConnectionRequestMessage": { - "type": "string", - "nullable": true, - "maxLength": 140, - "metadata": { - "description": "Optional. A message passed to the owner of the remote resource with the manual connection request." - } - }, - "customDnsConfigs": { - "type": "array", - "items": { - "$ref": "#/definitions/_1.privateEndpointCustomDnsConfigType" - }, - "nullable": true, - "metadata": { - "description": "Optional. Custom DNS configurations." - } - }, - "ipConfigurations": { - "type": "array", - "items": { - "$ref": "#/definitions/_1.privateEndpointIpConfigurationType" - }, - "nullable": true, - "metadata": { - "description": "Optional. A list of IP configurations of the Private Endpoint. This will be used to map to the first-party Service endpoints." - } - }, - "applicationSecurityGroupResourceIds": { - "type": "array", - "items": { - "type": "string" - }, - "nullable": true, - "metadata": { - "description": "Optional. Application security groups in which the Private Endpoint IP configuration is included." - } - }, - "customNetworkInterfaceName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The custom name of the network interface attached to the Private Endpoint." - } - }, - "lock": { - "$ref": "#/definitions/lockType", - "nullable": true, - "metadata": { - "description": "Optional. Specify the type of lock." - } - }, - "roleAssignments": { - "type": "array", - "items": { - "$ref": "#/definitions/roleAssignmentType" - }, - "nullable": true, - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "tags": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Tags to be applied on all resources/Resource Groups in this deployment." - } - }, - "enableTelemetry": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Enable/Disable usage telemetry for module." - } - } + "secrets": { + "copy": { + "name": "secrets", + "count": "[length(parameters('secretsToSet'))]" }, - "metadata": { - "description": "An AVM-aligned type for a private endpoint. To be used if the private endpoint's default service / groupId can be assumed (i.e., for services that only have one Private Endpoint type like 'vault' for key vault).", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } - } - }, - "roleAssignmentType": { - "type": "object", + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2023-07-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('secretsToSet')[copyIndex()].name)]", "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.5.1" - } - } - }, - "secretsOutputType": { - "type": "object", - "properties": {}, - "additionalProperties": { - "$ref": "#/definitions/_1.secretSetOutputType", - "metadata": { - "description": "An exported secret's references." - } - }, - "metadata": { - "description": "A map of the exported secrets", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } + "value": "[parameters('secretsToSet')[copyIndex()].value]" } } }, - "parameters": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of Cognitive Services account." - } - }, - "kind": { - "type": "string", - "allowedValues": [ - "AIServices", - "AnomalyDetector", - "CognitiveServices", - "ComputerVision", - "ContentModerator", - "ContentSafety", - "ConversationalLanguageUnderstanding", - "CustomVision.Prediction", - "CustomVision.Training", - "Face", - "FormRecognizer", - "HealthInsights", - "ImmersiveReader", - "Internal.AllInOne", - "LUIS", - "LUIS.Authoring", - "LanguageAuthoring", - "MetricsAdvisor", - "OpenAI", - "Personalizer", - "QnAMaker.v2", - "SpeechServices", - "TextAnalytics", - "TextTranslation" - ], - "metadata": { - "description": "Required. Kind of the Cognitive Services account. Use 'Get-AzCognitiveServicesAccountSku' to determine a valid combinations of 'kind' and 'SKU' for your Azure region." - } - }, - "sku": { - "type": "string", - "defaultValue": "S0", - "allowedValues": [ - "C2", - "C3", - "C4", - "F0", - "F1", - "S", - "S0", - "S1", - "S10", - "S2", - "S3", - "S4", - "S5", - "S6", - "S7", - "S8", - "S9" - ], - "metadata": { - "description": "Optional. SKU of the Cognitive Services account. Use 'Get-AzCognitiveServicesAccountSku' to determine a valid combinations of 'kind' and 'SKU' for your Azure region." - } - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]", - "metadata": { - "description": "Optional. Location for all Resources." - } - }, - "diagnosticSettings": { - "type": "array", - "items": { - "$ref": "#/definitions/diagnosticSettingFullType" - }, - "nullable": true, - "metadata": { - "description": "Optional. The diagnostic settings of the service." - } - }, - "publicNetworkAccess": { - "type": "string", - "nullable": true, - "allowedValues": [ - "Enabled", - "Disabled" - ], - "metadata": { - "description": "Optional. Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set and networkAcls are not set." - } - }, - "customSubDomainName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Conditional. Subdomain name used for token-based authentication. Required if 'networkAcls' or 'privateEndpoints' are set." - } - }, - "networkAcls": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. A collection of rules governing the accessibility from specific network locations." - } - }, - "privateEndpoints": { - "type": "array", - "items": { - "$ref": "#/definitions/privateEndpointSingleServiceType" - }, - "nullable": true, - "metadata": { - "description": "Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible." - } - }, - "lock": { - "$ref": "#/definitions/lockType", - "nullable": true, - "metadata": { - "description": "Optional. The lock settings of the service." - } - }, - "roleAssignments": { + "outputs": { + "secretsSet": { "type": "array", "items": { - "$ref": "#/definitions/roleAssignmentType" + "$ref": "#/definitions/secretSetOutputType" }, - "nullable": true, - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "tags": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Tags of the resource." - } - }, - "allowedFqdnList": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. List of allowed FQDN." - } - }, - "apiProperties": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The API properties for special APIs." - } - }, - "disableLocalAuth": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Optional. Allow only Azure AD authentication. Should be enabled for security reasons." - } - }, - "customerManagedKey": { - "$ref": "#/definitions/customerManagedKeyType", - "nullable": true, - "metadata": { - "description": "Optional. The customer managed key definition." - } - }, - "dynamicThrottlingEnabled": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "Optional. The flag to enable dynamic throttling." - } - }, - "migrationToken": { - "type": "securestring", - "nullable": true, - "metadata": { - "description": "Optional. Resource migration token." - } - }, - "restore": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "Optional. Restore a soft-deleted cognitive service at deployment time. Will fail if no such soft-deleted resource exists." - } - }, - "restrictOutboundNetworkAccess": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Optional. Restrict outbound network access." - } - }, - "userOwnedStorage": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. The storage accounts for this resource." - } - }, - "managedIdentities": { - "$ref": "#/definitions/managedIdentityAllType", - "nullable": true, - "metadata": { - "description": "Optional. The managed identity definition for this resource." - } - }, - "enableTelemetry": { - "type": "bool", - "defaultValue": true, "metadata": { - "description": "Optional. Enable/Disable usage telemetry for module." - } - }, - "deployments": { - "type": "array", - "items": { - "$ref": "#/definitions/deploymentType" + "description": "The references to the secrets exported to the provided Key Vault." }, - "nullable": true, - "metadata": { - "description": "Optional. Array of deployments about cognitive service accounts to create." - } - }, - "secretsExportConfiguration": { - "$ref": "#/definitions/secretsExportConfigurationType", - "nullable": true, - "metadata": { - "description": "Optional. Key vault reference and secret settings for the module's secrets export." - } - }, - "allowProjectManagement": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Enable/Disable project management feature for AI Foundry." + "copy": { + "count": "[length(range(0, length(coalesce(parameters('secretsToSet'), createArray()))))]", + "input": { + "secretResourceId": "[resourceId('Microsoft.KeyVault/vaults/secrets', parameters('keyVaultName'), parameters('secretsToSet')[range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()]].name)]", + "secretUri": "[reference(format('secrets[{0}]', range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()])).secretUri]", + "secretUriWithVersion": "[reference(format('secrets[{0}]', range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()])).secretUriWithVersion]" + } } } - }, - "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)))))]" - } - ], - "enableReferencedModulesTelemetry": false, - "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", - "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned, UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', null())), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", - "builtInRoleNames": { - "Cognitive Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '25fbc0a9-bd7c-42a3-aa1a-3b75d497ee68')]", - "Cognitive Services Custom Vision Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c1ff6cc2-c111-46fe-8896-e0ef812ad9f3')]", - "Cognitive Services Custom Vision Deployment": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5c4089e1-6d96-4d2f-b296-c1bc7137275f')]", - "Cognitive Services Custom Vision Labeler": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '88424f51-ebe7-446f-bc41-7fa16989e96c')]", - "Cognitive Services Custom Vision Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '93586559-c37d-4a6b-ba08-b9f0940c2d73')]", - "Cognitive Services Custom Vision Trainer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0a5ae4ab-0d65-4eeb-be61-29fc9b54394b')]", - "Cognitive Services Data Reader (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b59867f0-fa02-499b-be73-45a86b5b3e1c')]", - "Cognitive Services Face Recognizer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9894cab4-e18a-44aa-828b-cb588cd6f2d7')]", - "Cognitive Services Immersive Reader User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b2de6794-95db-4659-8781-7e080d3f2b9d')]", - "Cognitive Services Language Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f07febfe-79bc-46b1-8b37-790e26e6e498')]", - "Cognitive Services Language Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7628b7b8-a8b2-4cdc-b46f-e9b35248918e')]", - "Cognitive Services Language Writer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f2310ca1-dc64-4889-bb49-c8e0fa3d47a8')]", - "Cognitive Services LUIS Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f72c8140-2111-481c-87ff-72b910f6e3f8')]", - "Cognitive Services LUIS Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18e81cdc-4e98-4e29-a639-e7d10c5a6226')]", - "Cognitive Services LUIS Writer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6322a993-d5c9-4bed-b113-e49bbea25b27')]", - "Cognitive Services Metrics Advisor Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'cb43c632-a144-4ec5-977c-e80c4affc34a')]", - "Cognitive Services Metrics Advisor User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3b20f47b-3825-43cb-8114-4bd2201156a8')]", - "Cognitive Services OpenAI Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a001fd3d-188f-4b5d-821b-7da978bf7442')]", - "Cognitive Services OpenAI User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e0bd9bd-7b93-4f28-af87-19fc36ad61bd')]", - "Cognitive Services QnA Maker Editor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f4cc2bf9-21be-47a1-bdf1-5c5804381025')]", - "Cognitive Services QnA Maker Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '466ccd10-b268-4a11-b098-b4849f024126')]", - "Cognitive Services Speech Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0e75ca1e-0464-4b4d-8b93-68208a576181')]", - "Cognitive Services Speech User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f2dc8367-1007-4938-bd23-fe263f013447')]", - "Cognitive Services User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a97b65f3-24c7-4388-baec-2e87135dc908')]", - "Azure AI Developer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '64702f94-c441-49e6-a78b-ef80e0188fee')]", - "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + } + }, + "dependsOn": [ + "storageAccount" + ] + } + }, + "outputs": { + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed storage account." + }, + "value": "[resourceId('Microsoft.Storage/storageAccounts', parameters('name'))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed storage account." + }, + "value": "[parameters('name')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed storage account." + }, + "value": "[resourceGroup().name]" + }, + "primaryBlobEndpoint": { + "type": "string", + "metadata": { + "description": "The primary blob endpoint reference if blob services are deployed." + }, + "value": "[if(and(not(empty(parameters('blobServices'))), contains(parameters('blobServices'), 'containers')), reference(format('Microsoft.Storage/storageAccounts/{0}', parameters('name')), '2019-04-01').primaryEndpoints.blob, '')]" + }, + "systemAssignedMIPrincipalId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "The principal ID of the system assigned identity." + }, + "value": "[tryGet(tryGet(reference('storageAccount', '2023-05-01', 'full'), 'identity'), 'principalId')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('storageAccount', '2023-05-01', 'full').location]" + }, + "serviceEndpoints": { + "type": "object", + "metadata": { + "description": "All service endpoints of the deployed storage account, Note Standard_LRS and Standard_ZRS accounts only have a blob service endpoint." + }, + "value": "[reference('storageAccount').primaryEndpoints]" + }, + "privateEndpoints": { + "type": "array", + "items": { + "$ref": "#/definitions/privateEndpointOutputType" + }, + "metadata": { + "description": "The private endpoints of the Storage Account." + }, + "copy": { + "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]", + "input": { + "name": "[reference(format('storageAccount_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", + "resourceId": "[reference(format('storageAccount_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", + "groupId": "[tryGet(tryGet(reference(format('storageAccount_privateEndpoints[{0}]', copyIndex())).outputs, 'groupId'), 'value')]", + "customDnsConfigs": "[reference(format('storageAccount_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfigs.value]", + "networkInterfaceResourceIds": "[reference(format('storageAccount_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceResourceIds.value]" + } + } + }, + "exportedSecrets": { + "$ref": "#/definitions/secretsOutputType", + "metadata": { + "description": "A hashtable of references to the secrets exported to the provided Key Vault. The key of each reference is each secret's name." + }, + "value": "[if(not(equals(parameters('secretsExportConfiguration'), null())), toObject(reference('secretsExport').outputs.secretsSet.value, lambda('secret', last(split(lambdaVariables('secret').secretResourceId, '/'))), lambda('secret', lambdaVariables('secret'))), createObject())]" + } + } + } + }, + "dependsOn": [ + "blobPrivateDnsZone", + "filePrivateDnsZone" + ] + } + }, + "outputs": { + "storageName": { + "type": "string", + "value": "[reference('storageAccount').outputs.name.value]" + }, + "storageResourceId": { + "type": "string", + "value": "[reference('storageAccount').outputs.resourceId.value]" + } + } + } + }, + "dependsOn": [ + "aiSearch", + "cognitiveServices", + "logAnalyticsWorkspace", + "network" + ] + }, + "cognitiveServices": { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-cognitive-services-deployment', parameters('name'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('name')]" + }, + "resourceToken": { + "value": "[variables('resourceToken')]" + }, + "location": { + "value": "[parameters('aiDeploymentsLocation')]" + }, + "networkIsolation": { + "value": "[parameters('networkIsolation')]" + }, + "networkAcls": { + "value": "[parameters('networkAcls')]" + }, + "virtualNetworkResourceId": "[if(parameters('networkIsolation'), createObject('value', reference('network').outputs.resourceId.value), createObject('value', ''))]", + "virtualNetworkSubnetResourceId": "[if(parameters('networkIsolation'), createObject('value', reference('network').outputs.defaultSubnetResourceId.value), createObject('value', ''))]", + "principalIds": "[if(variables('deploySampleApp'), createObject('value', createArray(reference('appIdentity').outputs.principalId.value)), createObject('value', createArray()))]", + "logAnalyticsWorkspaceResourceId": { + "value": "[reference('logAnalyticsWorkspace').outputs.resourceId.value]" + }, + "aiModelDeployments": { + "copy": [ + { + "name": "value", + "count": "[length(createArray(parameters('aiEmbeddingModelDeployment'), parameters('aiGPTModelDeployment')))]", + "input": "[createObject('name', if(empty(tryGet(createArray(parameters('aiEmbeddingModelDeployment'), parameters('aiGPTModelDeployment'))[copyIndex('value')], 'name')), createArray(parameters('aiEmbeddingModelDeployment'), parameters('aiGPTModelDeployment'))[copyIndex('value')].modelName, tryGet(createArray(parameters('aiEmbeddingModelDeployment'), parameters('aiGPTModelDeployment'))[copyIndex('value')], 'name')), 'model', createObject('name', createArray(parameters('aiEmbeddingModelDeployment'), parameters('aiGPTModelDeployment'))[copyIndex('value')].modelName, 'format', 'OpenAI', 'version', createArray(parameters('aiEmbeddingModelDeployment'), parameters('aiGPTModelDeployment'))[copyIndex('value')].version), 'sku', createObject('name', 'GlobalStandard', 'capacity', createArray(parameters('aiEmbeddingModelDeployment'), parameters('aiGPTModelDeployment'))[copyIndex('value')].capacity))]" + } + ] + }, + "userObjectId": { + "value": "[parameters('userObjectId')]" + }, + "contentSafetyEnabled": { + "value": "[parameters('contentSafetyEnabled')]" + }, + "visionEnabled": { + "value": "[parameters('visionEnabled')]" + }, + "languageEnabled": { + "value": "[parameters('languageEnabled')]" + }, + "speechEnabled": { + "value": "[parameters('speechEnabled')]" + }, + "translatorEnabled": { + "value": "[parameters('translatorEnabled')]" + }, + "documentIntelligenceEnabled": { + "value": "[parameters('documentIntelligenceEnabled')]" + }, + "tags": { + "value": "[variables('allTags')]" + } + }, + "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.1.42791", + "templateHash": "15409590568992705867" + } + }, + "definitions": { + "deploymentsType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of cognitive service account deployment." + } + }, + "model": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of Cognitive Services account deployment model." + } + }, + "format": { + "type": "string", + "metadata": { + "description": "Required. The format of Cognitive Services account deployment model." + } + }, + "version": { + "type": "string", + "metadata": { + "description": "Required. The version of Cognitive Services account deployment model." + } + } + }, + "metadata": { + "description": "Required. Properties of Cognitive Services account deployment model." + } + }, + "sku": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource model definition representing SKU." + } + }, + "capacity": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The capacity of the resource model definition representing SKU." + } + }, + "tier": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The tier of the resource model definition representing SKU." + } + }, + "size": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The size of the resource model definition representing SKU." + } + }, + "family": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The family of the resource model definition representing SKU." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The resource model definition representing SKU." + } + }, + "raiPolicyName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of RAI policy." + } + }, + "versionUpgradeOption": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The version upgrade option." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "../customTypes.bicep" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "minLength": 3, + "maxLength": 12, + "metadata": { + "description": "Name of the Cognitive Services resource. Must be unique in the resource group." + } + }, + "resourceToken": { + "type": "string", + "metadata": { + "description": "Unique string to use when naming global resources." + } + }, + "location": { + "type": "string", + "metadata": { + "description": "Specifies the location for all the Azure resources. Defaults to the location of the resource group." + } + }, + "networkIsolation": { + "type": "bool", + "metadata": { + "description": "Specifies whether network isolation is enabled. When true, Foundry and related components will be deployed, network access parameters will be set to Disabled." + } + }, + "userObjectId": { + "type": "string", + "metadata": { + "description": "Specifies the object id of a Microsoft Entra ID user. In general, this the object id of the system administrator who deploys the Azure resources. This defaults to the deploying user." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Tags to be applied to the resources." + } + }, + "principalIds": { + "type": "array", + "items": { + "type": "string" + }, + "defaultValue": [], + "metadata": { + "description": "Optional. Array of identity principals to assign app-focused access." + } + }, + "virtualNetworkResourceId": { + "type": "string", + "metadata": { + "description": "Resource ID of the virtual network to link the private DNS zones." + } + }, + "virtualNetworkSubnetResourceId": { + "type": "string", + "metadata": { + "description": "Resource ID of the subnet for the private endpoint." + } + }, + "logAnalyticsWorkspaceResourceId": { + "type": "string", + "metadata": { + "description": "Resource ID of the Log Analytics workspace to use for diagnostic settings." + } + }, + "aiModelDeployments": { + "type": "array", + "items": { + "$ref": "#/definitions/deploymentsType" + }, + "defaultValue": [], + "metadata": { + "description": "Optional. Specifies the OpenAI deployments to create." + } + }, + "contentSafetyEnabled": { + "type": "bool", + "metadata": { + "description": "Whether to include Azure AI Content Safety in the deployment." + } + }, + "visionEnabled": { + "type": "bool", + "metadata": { + "description": "Whether to include Azure AI Vision in the deployment." + } + }, + "languageEnabled": { + "type": "bool", + "metadata": { + "description": "Whether to include Azure AI Language in the deployment." + } + }, + "speechEnabled": { + "type": "bool", + "metadata": { + "description": "Whether to include Azure AI Speech in the deployment." + } + }, + "translatorEnabled": { + "type": "bool", + "metadata": { + "description": "Whether to include Azure AI Translator in the deployment." + } + }, + "documentIntelligenceEnabled": { + "type": "bool", + "metadata": { + "description": "Whether to include Azure Document Intelligence in the deployment." + } + }, + "networkAcls": { + "type": "object", + "metadata": { + "description": "Optional. A collection of rules governing the accessibility from specific network locations." + } + } + }, + "variables": { + "copy": [ + { + "name": "roleAssignmentsForServicePrincipals", + "count": "[length(parameters('principalIds'))]", + "input": { + "principalId": "[parameters('principalIds')[copyIndex('roleAssignmentsForServicePrincipals')]]", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "Cognitive Services OpenAI User" + } + } + ], + "allRoleAssignments": "[concat(if(empty(parameters('userObjectId')), createArray(), createArray(createObject('principalId', parameters('userObjectId'), 'principalType', 'User', 'roleDefinitionIdOrName', 'Cognitive Services OpenAI Contributor'), createObject('principalId', parameters('userObjectId'), 'principalType', 'User', 'roleDefinitionIdOrName', 'Cognitive Services Contributor'), createObject('principalId', parameters('userObjectId'), 'principalType', 'User', 'roleDefinitionIdOrName', 'Cognitive Services User'))), variables('roleAssignmentsForServicePrincipals'))]" + }, + "resources": { + "cognitiveServicesPrivateDnsZone": { + "condition": "[parameters('networkIsolation')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "private-dns-cognitiveservices-deployment", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[format('privatelink.cognitiveservices.{0}', if(equals(toLower(environment().name), 'azureusgovernment'), 'azure.us', 'azure.com'))]" + }, + "virtualNetworkLinks": { + "value": [ + { + "virtualNetworkResourceId": "[parameters('virtualNetworkResourceId')]" + } + ] + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "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.32.4.45862", + "templateHash": "83178825086050429" + }, + "name": "Private DNS Zones", + "description": "This module deploys a Private DNS zone.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "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." } }, - "resources": { - "cMKKeyVault::cMKKey": { - "condition": "[and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), not(empty(tryGet(parameters('customerManagedKey'), 'keyName')))))]", - "existing": true, - "type": "Microsoft.KeyVault/vaults/keys", - "apiVersion": "2023-07-01", - "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[2]]", - "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[4]]", - "name": "[format('{0}/{1}', last(split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')), tryGet(parameters('customerManagedKey'), 'keyName'))]" - }, - "avmTelemetry": { - "condition": "[parameters('enableTelemetry')]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2024-03-01", - "name": "[format('46d3xbcp.res.cognitiveservices-account.{0}.{1}', replace('0.11.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "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." + } + } + } + }, + "nullable": true + }, + "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." + } + } + }, + "nullable": true + }, + "aType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "aRecords": { + "type": "array", + "items": { + "type": "object", "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" - } + "ipv4Address": { + "type": "string", + "metadata": { + "description": "Required. The IPv4 address of this A record." } } } }, - "cMKKeyVault": { - "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId')))]", - "existing": true, - "type": "Microsoft.KeyVault/vaults", - "apiVersion": "2023-07-01", - "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[2]]", - "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[4]]", - "name": "[last(split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/'))]" - }, - "cMKUserAssignedIdentity": { - "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId')))]", - "existing": true, - "type": "Microsoft.ManagedIdentity/userAssignedIdentities", - "apiVersion": "2025-01-31-preview", - "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/')[2]]", - "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/')[4]]", - "name": "[last(split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/'))]" - }, - "cognitiveService": { - "type": "Microsoft.CognitiveServices/accounts", - "apiVersion": "2025-04-01-preview", - "name": "[parameters('name')]", - "kind": "[parameters('kind')]", - "identity": "[variables('identity')]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", - "sku": { - "name": "[parameters('sku')]" - }, - "properties": { - "allowProjectManagement": "[parameters('allowProjectManagement')]", - "customSubDomainName": "[parameters('customSubDomainName')]", - "networkAcls": "[if(not(empty(coalesce(parameters('networkAcls'), createObject()))), createObject('defaultAction', tryGet(parameters('networkAcls'), 'defaultAction'), 'virtualNetworkRules', coalesce(tryGet(parameters('networkAcls'), 'virtualNetworkRules'), createArray()), 'ipRules', coalesce(tryGet(parameters('networkAcls'), 'ipRules'), createArray())), null())]", - "publicNetworkAccess": "[if(not(equals(parameters('publicNetworkAccess'), null())), parameters('publicNetworkAccess'), if(not(empty(parameters('networkAcls'))), 'Enabled', 'Disabled'))]", - "allowedFqdnList": "[parameters('allowedFqdnList')]", - "apiProperties": "[parameters('apiProperties')]", - "disableLocalAuth": "[parameters('disableLocalAuth')]", - "encryption": "[if(not(empty(parameters('customerManagedKey'))), createObject('keySource', 'Microsoft.KeyVault', 'keyVaultProperties', createObject('identityClientId', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), ''))), reference('cMKUserAssignedIdentity').clientId, null()), 'keyVaultUri', reference('cMKKeyVault').vaultUri, 'keyName', parameters('customerManagedKey').keyName, 'keyVersion', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'keyVersion'), ''))), tryGet(parameters('customerManagedKey'), 'keyVersion'), last(split(reference('cMKKeyVault::cMKKey').keyUriWithVersion, '/'))))), null())]", - "migrationToken": "[parameters('migrationToken')]", - "restore": "[parameters('restore')]", - "restrictOutboundNetworkAccess": "[parameters('restrictOutboundNetworkAccess')]", - "userOwnedStorage": "[parameters('userOwnedStorage')]", - "dynamicThrottlingEnabled": "[parameters('dynamicThrottlingEnabled')]" - }, - "dependsOn": [ - "cMKKeyVault", - "cMKKeyVault::cMKKey", - "cMKUserAssignedIdentity" - ] - }, - "cognitiveService_deployments": { - "copy": { - "name": "cognitiveService_deployments", - "count": "[length(coalesce(parameters('deployments'), createArray()))]", - "mode": "serial", - "batchSize": 1 - }, - "type": "Microsoft.CognitiveServices/accounts/deployments", - "apiVersion": "2025-04-01-preview", - "name": "[format('{0}/{1}', parameters('name'), coalesce(tryGet(coalesce(parameters('deployments'), createArray())[copyIndex()], 'name'), format('{0}-deployments', parameters('name'))))]", - "properties": { - "model": "[coalesce(parameters('deployments'), createArray())[copyIndex()].model]", - "raiPolicyName": "[tryGet(coalesce(parameters('deployments'), createArray())[copyIndex()], 'raiPolicyName')]", - "versionUpgradeOption": "[tryGet(coalesce(parameters('deployments'), createArray())[copyIndex()], 'versionUpgradeOption')]" - }, - "sku": "[coalesce(tryGet(coalesce(parameters('deployments'), createArray())[copyIndex()], 'sku'), createObject('name', parameters('sku'), 'capacity', tryGet(parameters('sku'), 'capacity'), 'tier', tryGet(parameters('sku'), 'tier'), 'size', tryGet(parameters('sku'), 'size'), 'family', tryGet(parameters('sku'), 'family')))]", - "dependsOn": [ - "cognitiveService" - ] - }, - "cognitiveService_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.CognitiveServices/accounts/{0}', parameters('name'))]", - "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", - "properties": { - "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", - "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" - }, - "dependsOn": [ - "cognitiveService" - ] - }, - "cognitiveService_diagnosticSettings": { - "copy": { - "name": "cognitiveService_diagnosticSettings", - "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" - }, - "type": "Microsoft.Insights/diagnosticSettings", - "apiVersion": "2021-05-01-preview", - "scope": "[format('Microsoft.CognitiveServices/accounts/{0}', parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "nullable": true, + "metadata": { + "description": "Optional. The list of A records in the record set." + } + } + } + }, + "nullable": true + }, + "aaaaType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "aaaaRecords": { + "type": "array", + "items": { + "type": "object", "properties": { - "copy": [ - { - "name": "metrics", - "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", - "input": { - "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", - "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", - "timeGrain": null - } - }, - { - "name": "logs", - "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", - "input": { - "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", - "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", - "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" - } + "ipv6Address": { + "type": "string", + "metadata": { + "description": "Required. The IPv6 address of this AAAA record." } - ], - "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", - "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", - "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", - "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", - "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", - "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" - }, - "dependsOn": [ - "cognitiveService" - ] + } + } }, - "cognitiveService_roleAssignments": { - "copy": { - "name": "cognitiveService_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.CognitiveServices/accounts/{0}', parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.CognitiveServices/accounts', 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": [ - "cognitiveService" - ] + "nullable": true, + "metadata": { + "description": "Optional. The list of AAAA records in the record set." + } + } + } + }, + "nullable": true + }, + "cnameType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "cnameRecord": { + "type": "object", + "properties": { + "cname": { + "type": "string", + "metadata": { + "description": "Required. The canonical name of the CNAME record." + } + } }, - "cognitiveService_privateEndpoints": { - "copy": { - "name": "cognitiveService_privateEndpoints", - "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-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]]", + "nullable": true, + "metadata": { + "description": "Optional. The CNAME record in the record set." + } + } + } + }, + "nullable": true + }, + "mxType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "mxRecords": { + "type": "array", + "items": { + "type": "object", "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'name'), format('pep-{0}-{1}-{2}', last(split(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'account'), copyIndex()))]" - }, - "privateLinkServiceConnections": "[if(not(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true())), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'account'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'account')))))), createObject('value', null()))]", - "manualPrivateLinkServiceConnections": "[if(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true()), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'account'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'account')), 'requestMessage', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualConnectionRequestMessage'), 'Manual approval required.'))))), createObject('value', null()))]", - "subnetResourceId": { - "value": "[coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId]" - }, - "enableTelemetry": { - "value": "[variables('enableReferencedModulesTelemetry')]" - }, - "location": { - "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'location'), reference(split(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location)]" - }, - "lock": { - "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" - }, - "privateDnsZoneGroup": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroup')]" - }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'roleAssignments')]" - }, - "tags": { - "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" - }, - "customDnsConfigs": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customDnsConfigs')]" - }, - "ipConfigurations": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'ipConfigurations')]" - }, - "applicationSecurityGroupResourceIds": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'applicationSecurityGroupResourceIds')]" - }, - "customNetworkInterfaceName": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customNetworkInterfaceName')]" + "exchange": { + "type": "string", + "metadata": { + "description": "Required. The domain name of the mail host for this MX record." } }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "languageVersion": "2.0", - "contentVersion": "1.0.0.0", + "preference": { + "type": "int", "metadata": { - "_generator": { - "name": "bicep", - "version": "0.34.44.8038", - "templateHash": "12389807800450456797" - }, - "name": "Private Endpoints", - "description": "This module deploys a Private Endpoint." - }, - "definitions": { - "privateDnsZoneGroupType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the Private DNS Zone Group." - } - }, - "privateDnsZoneGroupConfigs": { - "type": "array", - "items": { - "$ref": "#/definitions/privateDnsZoneGroupConfigType" - }, - "metadata": { - "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." - } - } - }, - "metadata": { - "__bicep_export!": true - } - }, - "ipConfigurationType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the resource that is unique within a resource group." - } - }, - "properties": { - "type": "object", - "properties": { - "groupId": { - "type": "string", - "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." - } - }, - "memberName": { - "type": "string", - "metadata": { - "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." - } - }, - "privateIPAddress": { - "type": "string", - "metadata": { - "description": "Required. A private IP address obtained from the private endpoint's subnet." - } - } - }, - "metadata": { - "description": "Required. Properties of private endpoint IP configurations." - } - } - }, - "metadata": { - "__bicep_export!": true - } - }, - "privateLinkServiceConnectionType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the private link service connection." - } - }, - "properties": { - "type": "object", - "properties": { - "groupIds": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." - } - }, - "privateLinkServiceId": { - "type": "string", - "metadata": { - "description": "Required. The resource id of private link service." - } - }, - "requestMessage": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." - } - } - }, - "metadata": { - "description": "Required. Properties of private link service connection." - } - } - }, - "metadata": { - "__bicep_export!": true - } - }, - "customDnsConfigType": { - "type": "object", - "properties": { - "fqdn": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. FQDN that resolves to private endpoint IP address." - } - }, - "ipAddresses": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. A list of private IP addresses of the private endpoint." - } - } - }, - "metadata": { - "__bicep_export!": true - } - }, - "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." - } - } - }, - "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.5.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": { - "__bicep_imported_from!": { - "sourceTemplate": "private-dns-zone-group/main.bicep" - } - } - }, - "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.5.1" - } - } - } + "description": "Required. The preference value for this MX record." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of MX records in the record set." + } + } + } + }, + "nullable": true + }, + "ptrType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "ptrRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "ptrdname": { + "type": "string", + "metadata": { + "description": "Required. The PTR target domain name for this PTR record." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of PTR records in the record set." + } + } + } + }, + "nullable": true + }, + "soaType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "soaRecord": { + "type": "object", + "properties": { + "email": { + "type": "string", + "metadata": { + "description": "Required. The email contact for this SOA record." + } + }, + "expireTime": { + "type": "int", + "metadata": { + "description": "Required. The expire time for this SOA record." + } + }, + "host": { + "type": "string", + "metadata": { + "description": "Required. The domain name of the authoritative name server for this SOA record." + } + }, + "minimumTtl": { + "type": "int", + "metadata": { + "description": "Required. The minimum value for this SOA record. By convention this is used to determine the negative caching duration." + } + }, + "refreshTime": { + "type": "int", + "metadata": { + "description": "Required. The refresh value for this SOA record." + } + }, + "retryTime": { + "type": "int", + "metadata": { + "description": "Required. The retry time for this SOA record." + } + }, + "serialNumber": { + "type": "int", + "metadata": { + "description": "Required. The serial number for this SOA record." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The SOA record in the record set." + } + } + } + }, + "nullable": true + }, + "srvType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "srvRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "priority": { + "type": "int", + "metadata": { + "description": "Required. The priority value for this SRV record." + } + }, + "weight": { + "type": "int", + "metadata": { + "description": "Required. The weight value for this SRV record." + } + }, + "port": { + "type": "int", + "metadata": { + "description": "Required. The port value for this SRV record." + } + }, + "target": { + "type": "string", + "metadata": { + "description": "Required. The target domain name for this SRV record." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of SRV records in the record set." + } + } + } + }, + "nullable": true + }, + "txtType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "txtRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "value": { + "type": "array", + "items": { + "type": "string" }, - "parameters": { - "name": { - "type": "string", - "metadata": { - "description": "Required. Name of the private endpoint resource to create." - } - }, - "subnetResourceId": { - "type": "string", - "metadata": { - "description": "Required. Resource ID of the subnet where the endpoint needs to be created." - } - }, - "applicationSecurityGroupResourceIds": { - "type": "array", - "items": { - "type": "string" - }, - "nullable": true, - "metadata": { - "description": "Optional. Application security groups in which the private endpoint IP configuration is included." - } - }, - "customNetworkInterfaceName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The custom name of the network interface attached to the private endpoint." - } - }, - "ipConfigurations": { - "type": "array", - "items": { - "$ref": "#/definitions/ipConfigurationType" - }, - "nullable": true, - "metadata": { - "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." - } - }, - "privateDnsZoneGroup": { - "$ref": "#/definitions/privateDnsZoneGroupType", - "nullable": true, - "metadata": { - "description": "Optional. The private DNS zone group to configure for the private endpoint." - } - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]", - "metadata": { - "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", - "nullable": true, - "metadata": { - "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." - } - }, - "customDnsConfigs": { - "type": "array", - "items": { - "$ref": "#/definitions/customDnsConfigType" - }, - "nullable": true, - "metadata": { - "description": "Optional. Custom DNS configurations." - } - }, - "manualPrivateLinkServiceConnections": { - "type": "array", - "items": { - "$ref": "#/definitions/privateLinkServiceConnectionType" - }, - "nullable": true, - "metadata": { - "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." - } - }, - "privateLinkServiceConnections": { - "type": "array", - "items": { - "$ref": "#/definitions/privateLinkServiceConnectionType" - }, - "nullable": true, - "metadata": { - "description": "Conditional. A grouping of information about the connection to the remote resource. Required if `manualPrivateLinkServiceConnections` is empty." - } - }, - "enableTelemetry": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Optional. Enable/Disable usage telemetry for module." - } + "metadata": { + "description": "Required. The text value of this TXT record." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of TXT records in the record set." + } + } + } + }, + "nullable": true + }, + "virtualNetworkLinkType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "minLength": 1, + "maxLength": 80, + "metadata": { + "description": "Optional. The resource name." + } + }, + "virtualNetworkResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the virtual network to link." + } + }, + "location": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Azure Region where the resource lives." + } + }, + "registrationEnabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Is auto-registration of virtual machine records in the virtual network in the Private DNS zone enabled?." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Resource tags." + } + }, + "resolutionPolicy": { + "type": "string", + "allowedValues": [ + "Default", + "NxDomainRedirect" + ], + "nullable": true, + "metadata": { + "description": "Optional. The resolution type of the private-dns-zone fallback machanism." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Private DNS zone name." + } + }, + "a": { + "$ref": "#/definitions/aType", + "metadata": { + "description": "Optional. Array of A records." + } + }, + "aaaa": { + "$ref": "#/definitions/aaaaType", + "metadata": { + "description": "Optional. Array of AAAA records." + } + }, + "cname": { + "$ref": "#/definitions/cnameType", + "metadata": { + "description": "Optional. Array of CNAME records." + } + }, + "mx": { + "$ref": "#/definitions/mxType", + "metadata": { + "description": "Optional. Array of MX records." + } + }, + "ptr": { + "$ref": "#/definitions/ptrType", + "metadata": { + "description": "Optional. Array of PTR records." + } + }, + "soa": { + "$ref": "#/definitions/soaType", + "metadata": { + "description": "Optional. Array of SOA records." + } + }, + "srv": { + "$ref": "#/definitions/srvType", + "metadata": { + "description": "Optional. Array of SRV records." + } + }, + "txt": { + "$ref": "#/definitions/txtType", + "metadata": { + "description": "Optional. Array of TXT records." + } + }, + "virtualNetworkLinks": { + "$ref": "#/definitions/virtualNetworkLinkType", + "metadata": { + "description": "Optional. Array of custom objects describing vNet links of the DNS zone. Each object should contain properties 'virtualNetworkResourceId' and 'registrationEnabled'. The 'vnetResourceId' is a resource ID of a vNet to link, 'registrationEnabled' (bool) enables automatic DNS registration in the zone for the linked vNet." + } + }, + "location": { + "type": "string", + "defaultValue": "global", + "metadata": { + "description": "Optional. The location of the PrivateDNSZone. Should be global." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "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')]", + "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": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privatednszone.{0}.{1}', replace('0.7.0', '.', '-'), 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" + } + } + } + } + }, + "privateDnsZone": { + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]" + }, + "privateDnsZone_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/privateDnsZones/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "privateDnsZone_roleAssignments": { + "copy": { + "name": "privateDnsZone_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones', 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": [ + "privateDnsZone" + ] + }, + "privateDnsZone_A": { + "copy": { + "name": "privateDnsZone_A", + "count": "[length(coalesce(parameters('a'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-ARecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('a'), createArray())[copyIndex()].name]" + }, + "aRecords": { + "value": "[tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'aRecords')]" + }, + "metadata": { + "value": "[tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'metadata')]" + }, + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'ttl'), 3600)]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'roleAssignments')]" + } + }, + "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.32.4.45862", + "templateHash": "2531120132215940282" + }, + "name": "Private DNS Zone A record", + "description": "This module deploys a Private DNS Zone A record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "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." } }, - "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)))))]" - } + "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" ], - "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')]" + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." } }, - "resources": { - "avmTelemetry": { - "condition": "[parameters('enableTelemetry')]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2024-03-01", - "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.11.0', '.', '-'), 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-05-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": "[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": "2022-09-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.34.44.8038", - "templateHash": "13997305779829540948" - }, - "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 - } - } - }, - "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." - } - } - }, - "variables": { - "copy": [ - { - "name": "privateDnsZoneConfigsVar", - "count": "[length(parameters('privateDnsZoneConfigs'))]", - "input": { - "name": "[coalesce(tryGet(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')], 'name'), last(split(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId, '/')))]", - "properties": { - "privateDnsZoneId": "[parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId]" - } - } - } - ] - }, - "resources": { - "privateEndpoint": { - "existing": true, - "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2024-05-01", - "name": "[parameters('privateEndpointName')]" - }, - "privateDnsZoneGroup": { - "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2024-05-01", - "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", - "properties": { - "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" - } - } - }, - "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-05-01', 'full').location]" - }, - "customDnsConfigs": { - "type": "array", - "items": { - "$ref": "#/definitions/customDnsConfigType" - }, - "metadata": { - "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": [ - "cognitiveService" - ] - }, - "secretsExport": { - "condition": "[not(equals(parameters('secretsExportConfiguration'), null()))]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-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]]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "keyVaultName": { - "value": "[last(split(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '/'))]" - }, - "secretsToSet": { - "value": "[union(createArray(), if(contains(parameters('secretsExportConfiguration'), 'accessKey1Name'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'accessKey1Name'), 'value', listKeys('cognitiveService', '2025-04-01-preview').key1)), createArray()), if(contains(parameters('secretsExportConfiguration'), 'accessKey2Name'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'accessKey2Name'), 'value', listKeys('cognitiveService', '2025-04-01-preview').key2)), createArray()))]" - } - }, - "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.1.42791", - "templateHash": "1200612323329026557" - } - }, - "definitions": { - "secretSetOutputType": { - "type": "object", - "properties": { - "secretResourceId": { - "type": "string", - "metadata": { - "description": "The resourceId of the exported secret." - } - }, - "secretUri": { - "type": "string", - "metadata": { - "description": "The secret URI of the exported secret." - } - }, - "secretUriWithVersion": { - "type": "string", - "metadata": { - "description": "The secret URI with version of the exported secret." - } - } - }, - "metadata": { - "description": "An AVM-aligned type for the output of the secret set via the secrets export feature.", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } - } - }, - "secretToSetType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the secret to set." - } - }, - "value": { - "type": "securestring", - "metadata": { - "description": "Required. The value of the secret to set." - } - } - }, - "metadata": { - "description": "An AVM-aligned type for the secret to set via the secrets export feature.", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } - } + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." } }, - "parameters": { - "keyVaultName": { - "type": "string", - "metadata": { - "description": "Required. The name of the Key Vault to set the ecrets in." - } - }, - "secretsToSet": { - "type": "array", - "items": { - "$ref": "#/definitions/secretToSetType" - }, - "metadata": { - "description": "Required. The secrets to set in the Key Vault." - } + "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\"." } }, - "resources": { - "keyVault": { - "existing": true, - "type": "Microsoft.KeyVault/vaults", - "apiVersion": "2023-07-01", - "name": "[parameters('keyVaultName')]" - }, - "secrets": { - "copy": { - "name": "secrets", - "count": "[length(parameters('secretsToSet'))]" - }, - "type": "Microsoft.KeyVault/vaults/secrets", - "apiVersion": "2023-07-01", - "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('secretsToSet')[copyIndex()].name)]", - "properties": { - "value": "[parameters('secretsToSet')[copyIndex()].value]" - } + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." } }, - "outputs": { - "secretsSet": { - "type": "array", - "items": { - "$ref": "#/definitions/secretSetOutputType" - }, - "metadata": { - "description": "The references to the secrets exported to the provided Key Vault." - }, - "copy": { - "count": "[length(range(0, length(coalesce(parameters('secretsToSet'), createArray()))))]", - "input": { - "secretResourceId": "[resourceId('Microsoft.KeyVault/vaults/secrets', parameters('keyVaultName'), parameters('secretsToSet')[range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()]].name)]", - "secretUri": "[reference(format('secrets[{0}]', range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()])).secretUri]", - "secretUriWithVersion": "[reference(format('secrets[{0}]', range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()])).secretUriWithVersion]" - } - } + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." } } } }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the A record." + } + }, + "aRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of A records in the record set." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "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')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "A": { + "type": "Microsoft.Network/privateDnsZones/A", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "properties": { + "aRecords": "[parameters('aRecords')]", + "metadata": "[parameters('metadata')]", + "ttl": "[parameters('ttl')]" + } + }, + "A_roleAssignments": { + "copy": { + "name": "A_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/A/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/A', parameters('privateDnsZoneName'), 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": [ - "cognitiveService" + "A" ] } }, @@ -47336,554 +36709,563 @@ "name": { "type": "string", "metadata": { - "description": "The name of the cognitive services account." + "description": "The name of the deployed A record." }, "value": "[parameters('name')]" }, "resourceId": { "type": "string", "metadata": { - "description": "The resource ID of the cognitive services account." + "description": "The resource ID of the deployed A record." }, - "value": "[resourceId('Microsoft.CognitiveServices/accounts', parameters('name'))]" + "value": "[resourceId('Microsoft.Network/privateDnsZones/A', parameters('privateDnsZoneName'), parameters('name'))]" }, "resourceGroupName": { "type": "string", "metadata": { - "description": "The resource group the cognitive services account was deployed into." + "description": "The resource group of the deployed A record." }, "value": "[resourceGroup().name]" - }, - "endpoint": { - "type": "string", - "metadata": { - "description": "The service endpoint of the cognitive services account." - }, - "value": "[reference('cognitiveService').endpoint]" - }, - "endpoints": { - "$ref": "#/definitions/endpointType", - "metadata": { - "description": "All endpoints available for the cognitive services account, types depends on the cognitive service kind." - }, - "value": "[reference('cognitiveService').endpoints]" - }, - "systemAssignedMIPrincipalId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "The principal ID of the system assigned identity." - }, - "value": "[tryGet(tryGet(reference('cognitiveService', '2025-04-01-preview', 'full'), 'identity'), 'principalId')]" - }, - "location": { - "type": "string", - "metadata": { - "description": "The location the resource was deployed into." - }, - "value": "[reference('cognitiveService', '2025-04-01-preview', 'full').location]" - }, - "exportedSecrets": { - "$ref": "#/definitions/secretsOutputType", - "metadata": { - "description": "A hashtable of references to the secrets exported to the provided Key Vault. The key of each reference is each secret's name." - }, - "value": "[if(not(equals(parameters('secretsExportConfiguration'), null())), toObject(reference('secretsExport').outputs.secretsSet.value, lambda('secret', last(split(lambdaVariables('secret').secretResourceId, '/'))), lambda('secret', lambdaVariables('secret'))), createObject())]" - }, - "privateEndpoints": { - "type": "array", - "items": { - "$ref": "#/definitions/privateEndpointOutputType" - }, - "metadata": { - "description": "The private endpoints of the congitive services account." - }, - "copy": { - "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]", - "input": { - "name": "[reference(format('cognitiveService_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", - "resourceId": "[reference(format('cognitiveService_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", - "groupId": "[tryGet(tryGet(reference(format('cognitiveService_privateEndpoints[{0}]', copyIndex())).outputs, 'groupId'), 'value')]", - "customDnsConfigs": "[reference(format('cognitiveService_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfigs.value]", - "networkInterfaceResourceIds": "[reference(format('cognitiveService_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceResourceIds.value]" - } - } } } } - } - } - }, - "outputs": { - "resourceId": { - "type": "string", - "value": "[reference('cognitiveService').outputs.resourceId.value]" - }, - "name": { - "type": "string", - "value": "[reference('cognitiveService').outputs.name.value]" - }, - "systemAssignedMIPrincipalId": { - "type": "string", - "nullable": true, - "value": "[tryGet(tryGet(reference('cognitiveService').outputs, 'systemAssignedMIPrincipalId'), 'value')]" - }, - "endpoint": { - "type": "string", - "value": "[reference('cognitiveService').outputs.endpoint.value]" + }, + "dependsOn": [ + "privateDnsZone" + ] }, - "foundryConnection": { - "type": "object", - "value": { - "name": "[reference('cognitiveService').outputs.name.value]", - "value": null, - "category": "[parameters('category')]", - "target": "[reference('cognitiveService').outputs.endpoint.value]", - "kind": "[parameters('kind')]", - "connectionProperties": { - "authType": "AAD" - }, - "isSharedToAll": true, - "metadata": { - "ApiType": "Azure", - "Kind": "[parameters('kind')]", - "ResourceId": "[reference('cognitiveService').outputs.resourceId.value]" - } - } - } - } - } - }, - "dependsOn": [ - "cognitiveServicesPrivateDnsZone" - ] - }, - "speech": { - "condition": "[parameters('speechEnabled')]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[take(format('{0}-speech-deployment', parameters('name')), 64)]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[format('speech{0}{1}', parameters('name'), parameters('resourceToken'))]" - }, - "location": { - "value": "[parameters('location')]" - }, - "kind": { - "value": "SpeechServices" - }, - "networkIsolation": { - "value": "[parameters('networkIsolation')]" - }, - "networkAcls": { - "value": "[parameters('networkAcls')]" - }, - "virtualNetworkSubnetResourceId": "[if(parameters('networkIsolation'), createObject('value', parameters('virtualNetworkSubnetResourceId')), createObject('value', ''))]", - "privateDnsZonesResourceIds": "[if(parameters('networkIsolation'), createObject('value', createArray(reference('cognitiveServicesPrivateDnsZone').outputs.resourceId.value)), createObject('value', createArray()))]", - "logAnalyticsWorkspaceResourceId": { - "value": "[parameters('logAnalyticsWorkspaceResourceId')]" - }, - "tags": { - "value": "[parameters('tags')]" - } - }, - "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.1.42791", - "templateHash": "2348080591288311162" - } - }, - "definitions": { - "deploymentsType": { - "type": "object", + "privateDnsZone_AAAA": { + "copy": { + "name": "privateDnsZone_AAAA", + "count": "[length(coalesce(parameters('aaaa'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-AAAARecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { - "name": { - "type": "string", - "nullable": true, + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('aaaa'), createArray())[copyIndex()].name]" + }, + "aaaaRecords": { + "value": "[tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'aaaaRecords')]" + }, "metadata": { - "description": "Optional. Specify the name of cognitive service account deployment." + "value": "[tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'metadata')]" + }, + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'ttl'), 3600)]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'roleAssignments')]" } }, - "model": { - "type": "object", - "properties": { - "name": { + "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.32.4.45862", + "templateHash": "16709340450244912125" + }, + "name": "Private DNS Zone AAAA record", + "description": "This module deploys a Private DNS Zone AAAA record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "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." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { "type": "string", "metadata": { - "description": "Required. The name of Cognitive Services account deployment model." + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." } }, - "format": { + "name": { "type": "string", "metadata": { - "description": "Required. The format of Cognitive Services account deployment model." + "description": "Required. The name of the AAAA record." } }, - "version": { - "type": "string", + "aaaaRecords": { + "type": "array", + "nullable": true, "metadata": { - "description": "Required. The version of Cognitive Services account deployment model." + "description": "Optional. The list of AAAA records in the record set." } - } - }, - "metadata": { - "description": "Required. Properties of Cognitive Services account deployment model." - } - }, - "sku": { - "type": "object", - "properties": { - "name": { - "type": "string", + }, + "metadata": { + "type": "object", + "nullable": true, "metadata": { - "description": "Required. The name of the resource model definition representing SKU." + "description": "Optional. The metadata attached to the record set." } }, - "capacity": { + "ttl": { "type": "int", - "nullable": true, + "defaultValue": 3600, "metadata": { - "description": "Optional. The capacity of the resource model definition representing SKU." + "description": "Optional. The TTL (time-to-live) of the records in the record set." } }, - "tier": { - "type": "string", - "nullable": true, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", "metadata": { - "description": "Optional. The tier of the resource model definition representing SKU." + "description": "Optional. Array of role assignments to create." + } + } + }, + "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')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "AAAA": { + "type": "Microsoft.Network/privateDnsZones/AAAA", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "properties": { + "aaaaRecords": "[parameters('aaaaRecords')]", + "metadata": "[parameters('metadata')]", + "ttl": "[parameters('ttl')]" } }, - "size": { + "AAAA_roleAssignments": { + "copy": { + "name": "AAAA_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/AAAA/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/AAAA', parameters('privateDnsZoneName'), 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": [ + "AAAA" + ] + } + }, + "outputs": { + "name": { "type": "string", - "nullable": true, "metadata": { - "description": "Optional. The size of the resource model definition representing SKU." - } + "description": "The name of the deployed AAAA record." + }, + "value": "[parameters('name')]" }, - "family": { + "resourceId": { "type": "string", - "nullable": true, "metadata": { - "description": "Optional. The family of the resource model definition representing SKU." - } + "description": "The resource ID of the deployed AAAA record." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/AAAA', parameters('privateDnsZoneName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed AAAA record." + }, + "value": "[resourceGroup().name]" } - }, - "nullable": true, - "metadata": { - "description": "Optional. The resource model definition representing SKU." - } - }, - "raiPolicyName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of RAI policy." - } - }, - "versionUpgradeOption": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The version upgrade option." } } }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "../customTypes.bicep" - } - } + "dependsOn": [ + "privateDnsZone" + ] }, - "roleAssignmentType": { - "type": "object", + "privateDnsZone_CNAME": { + "copy": { + "name": "privateDnsZone_CNAME", + "count": "[length(coalesce(parameters('cname'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-CNAMERecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "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\"." - } + "expressionEvaluationOptions": { + "scope": "inner" }, - "conditionVersion": { - "type": "string", - "allowedValues": [ - "2.0" - ], - "nullable": true, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('cname'), createArray())[copyIndex()].name]" + }, + "cnameRecord": { + "value": "[tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'cnameRecord')]" + }, "metadata": { - "description": "Optional. Version of the condition." + "value": "[tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'metadata')]" + }, + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'ttl'), 3600)]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'roleAssignments')]" } }, - "delegatedManagedIdentityResourceId": { - "type": "string", - "nullable": true, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", "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.5.1" + "_generator": { + "name": "bicep", + "version": "0.32.4.45862", + "templateHash": "9976020649752073181" + }, + "name": "Private DNS Zone CNAME record", + "description": "This module deploys a Private DNS Zone CNAME record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "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." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the CNAME record." + } + }, + "cnameRecord": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. A CNAME record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "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')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "CNAME": { + "type": "Microsoft.Network/privateDnsZones/CNAME", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "properties": { + "cnameRecord": "[parameters('cnameRecord')]", + "metadata": "[parameters('metadata')]", + "ttl": "[parameters('ttl')]" + } + }, + "CNAME_roleAssignments": { + "copy": { + "name": "CNAME_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/CNAME/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/CNAME', parameters('privateDnsZoneName'), 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": [ + "CNAME" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed CNAME record." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed CNAME record." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/CNAME', parameters('privateDnsZoneName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed CNAME record." + }, + "value": "[resourceGroup().name]" + } + } } - } - } - }, - "parameters": { - "name": { - "type": "string", - "metadata": { - "description": "Name of the Cognitive Services resource. Must be unique in the resource group." - } - }, - "location": { - "type": "string", - "metadata": { - "description": "The location of the Cognitive Services resource." - } - }, - "kind": { - "type": "string", - "allowedValues": [ - "AIServices", - "AnomalyDetector", - "CognitiveServices", - "ComputerVision", - "ContentModerator", - "ContentSafety", - "ConversationalLanguageUnderstanding", - "CustomVision.Prediction", - "CustomVision.Training", - "Face", - "FormRecognizer", - "HealthInsights", - "ImmersiveReader", - "Internal.AllInOne", - "LUIS", - "LUIS.Authoring", - "LanguageAuthoring", - "MetricsAdvisor", - "OpenAI", - "Personalizer", - "QnAMaker.v2", - "SpeechServices", - "TextAnalytics", - "TextTranslation" - ], - "metadata": { - "description": "Required. Kind of the Cognitive Services account. Use 'Get-AzCognitiveServicesAccountSku' to determine a valid combinations of 'kind' and 'SKU' for your Azure region." - } - }, - "sku": { - "type": "string", - "defaultValue": "S0", - "allowedValues": [ - "S", - "S0", - "S1", - "S2", - "S3", - "S4", - "S5", - "S6", - "S7", - "S8" - ], - "metadata": { - "description": "Required. The SKU of the Cognitive Services account. Use 'Get-AzCognitiveServicesAccountSku' to determine a valid combinations of 'kind' and 'SKU' for your Azure region." - } - }, - "category": { - "type": "string", - "defaultValue": "CognitiveService", - "metadata": { - "description": "Category of the Cognitive Services account." - } - }, - "networkIsolation": { - "type": "bool", - "metadata": { - "description": "Specifies whether to enable network isolation. If true, the resource will be deployed in a private endpoint and public network access will be disabled." - } - }, - "privateDnsZonesResourceIds": { - "type": "array", - "items": { - "type": "string" - }, - "defaultValue": [], - "metadata": { - "description": "Existing resource ID of the private DNS zone for the private endpoint." - } - }, - "virtualNetworkSubnetResourceId": { - "type": "string", - "metadata": { - "description": "Resource ID of the subnet for the private endpoint." - } - }, - "logAnalyticsWorkspaceResourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the Log Analytics workspace to use for diagnostic settings." - } - }, - "aiModelDeployments": { - "type": "array", - "items": { - "$ref": "#/definitions/deploymentsType" }, - "defaultValue": [], - "metadata": { - "description": "Optional. Specifies the OpenAI deployments to create." - } + "dependsOn": [ + "privateDnsZone" + ] }, - "roleAssignments": { - "type": "array", - "items": { - "$ref": "#/definitions/roleAssignmentType" + "privateDnsZone_MX": { + "copy": { + "name": "privateDnsZone_MX", + "count": "[length(coalesce(parameters('mx'), createArray()))]" }, - "nullable": true, - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "tags": { - "type": "object", - "defaultValue": {}, - "metadata": { - "description": "Optional. Tags to be applied to the resources." - } - }, - "networkAcls": { - "type": "object", - "metadata": { - "description": "Optional. A collection of rules governing the accessibility from specific network locations." - } - } - }, - "variables": { - "copy": [ - { - "name": "privateDnsZones", - "count": "[length(parameters('privateDnsZonesResourceIds'))]", - "input": { - "privateDnsZoneResourceId": "[parameters('privateDnsZonesResourceIds')[copyIndex('privateDnsZones')]]" - } - } - ], - "nameFormatted": "[take(toLower(parameters('name')), 24)]" - }, - "resources": { - "cognitiveService": { "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[take(format('cog-{0}-{1}-deployment', parameters('kind'), parameters('name')), 64)]", + "name": "[format('{0}-PrivateDnsZone-MXRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "name": { - "value": "[variables('nameFormatted')]" - }, - "location": { - "value": "[parameters('location')]" - }, - "tags": { - "value": "[parameters('tags')]" - }, - "sku": { - "value": "[parameters('sku')]" - }, - "kind": { - "value": "[parameters('kind')]" - }, - "allowProjectManagement": { - "value": true - }, - "managedIdentities": { - "value": { - "systemAssigned": true - } - }, - "deployments": { - "value": "[parameters('aiModelDeployments')]" - }, - "customSubDomainName": { + "privateDnsZoneName": { "value": "[parameters('name')]" }, - "disableLocalAuth": { - "value": "[parameters('networkIsolation')]" + "name": { + "value": "[coalesce(parameters('mx'), createArray())[copyIndex()].name]" }, - "publicNetworkAccess": "[if(parameters('networkIsolation'), createObject('value', 'Disabled'), createObject('value', 'Enabled'))]", - "diagnosticSettings": { - "value": [ - { - "workspaceResourceId": "[parameters('logAnalyticsWorkspaceResourceId')]" - } - ] + "metadata": { + "value": "[tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'metadata')]" }, - "roleAssignments": { - "value": "[parameters('roleAssignments')]" + "mxRecords": { + "value": "[tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'mxRecords')]" }, - "networkAcls": { - "value": "[parameters('networkAcls')]" + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'ttl'), 3600)]" }, - "privateEndpoints": "[if(parameters('networkIsolation'), createObject('value', createArray(createObject('privateDnsZoneGroup', createObject('privateDnsZoneGroupConfigs', variables('privateDnsZones')), 'subnetResourceId', parameters('virtualNetworkSubnetResourceId')))), createObject('value', createArray()))]" + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'roleAssignments')]" + } }, "template": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", @@ -47892,1057 +37274,1126 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "16135659971302525380" + "version": "0.32.4.45862", + "templateHash": "2520323624213076361" }, - "name": "Cognitive Services", - "description": "This module deploys a Cognitive Service." + "name": "Private DNS Zone MX record", + "description": "This module deploys a Private DNS Zone MX record.", + "owner": "Azure/module-maintainers" }, "definitions": { - "privateEndpointOutputType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the private endpoint." - } - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the private endpoint." - } - }, - "groupId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "The group Id for the private endpoint Group." - } - }, - "customDnsConfigs": { - "type": "array", - "items": { - "type": "object", - "properties": { - "fqdn": { - "type": "string", - "nullable": true, - "metadata": { - "description": "FQDN that resolves to private endpoint IP address." - } - }, - "ipAddresses": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "A list of private IP addresses of the private endpoint." - } - } + "roleAssignmentType": { + "type": "array", + "items": { + "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." } }, - "metadata": { - "description": "The custom DNS configurations of the private endpoint." - } - }, - "networkInterfaceResourceIds": { - "type": "array", - "items": { - "type": "string" + "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'." + } }, - "metadata": { - "description": "The IDs of the network interfaces associated with the private endpoint." - } - } - }, - "metadata": { - "__bicep_export!": true, - "description": "The type for the private endpoint output." - } - }, - "deploymentType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Specify the name of cognitive service account deployment." - } - }, - "model": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of Cognitive Services account deployment model." - } - }, - "format": { - "type": "string", - "metadata": { - "description": "Required. The format of Cognitive Services account deployment model." - } - }, - "version": { - "type": "string", - "metadata": { - "description": "Required. The version of Cognitive Services account deployment model." - } + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." } }, - "metadata": { - "description": "Required. Properties of Cognitive Services account deployment model." - } - }, - "sku": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the resource model definition representing SKU." - } - }, - "capacity": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The capacity of the resource model definition representing SKU." - } - }, - "tier": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The tier of the resource model definition representing SKU." - } - }, - "size": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The size of the resource model definition representing SKU." - } - }, - "family": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The family of the resource model definition representing SKU." - } + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." } }, - "nullable": true, - "metadata": { - "description": "Optional. The resource model definition representing SKU." - } - }, - "raiPolicyName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of RAI policy." - } - }, - "versionUpgradeOption": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The version upgrade option." + "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." + } } } }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", "metadata": { - "__bicep_export!": true, - "description": "The type for a cognitive services account deployment." + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." } }, - "endpointType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Type of the endpoint." - } - }, - "endpoint": { - "type": "string", - "nullable": true, - "metadata": { - "description": "The endpoint URI." - } - } - }, + "name": { + "type": "string", "metadata": { - "__bicep_export!": true, - "description": "The type for a cognitive services account endpoint." + "description": "Required. The name of the MX record." } }, - "secretsExportConfigurationType": { + "metadata": { "type": "object", - "properties": { - "keyVaultResourceId": { - "type": "string", - "metadata": { - "description": "Required. The key vault name where to store the keys and connection strings generated by the modules." - } - }, - "accessKey1Name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name for the accessKey1 secret to create." - } - }, - "accessKey2Name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name for the accessKey2 secret to create." - } - } - }, + "nullable": true, "metadata": { - "__bicep_export!": true, - "description": "The type of the secrets exported to the provided Key Vault." + "description": "Optional. The metadata attached to the record set." } }, - "_1.privateEndpointCustomDnsConfigType": { - "type": "object", - "properties": { - "fqdn": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. FQDN that resolves to private endpoint IP address." - } - }, - "ipAddresses": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. A list of private IP addresses of the private endpoint." - } - } - }, + "mxRecords": { + "type": "array", + "nullable": true, "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } + "description": "Optional. The list of MX records in the record set." } }, - "_1.privateEndpointIpConfigurationType": { - "type": "object", + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "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')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "MX": { + "type": "Microsoft.Network/privateDnsZones/MX", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the resource that is unique within a resource group." - } - }, + "metadata": "[parameters('metadata')]", + "mxRecords": "[parameters('mxRecords')]", + "ttl": "[parameters('ttl')]" + } + }, + "MX_roleAssignments": { + "copy": { + "name": "MX_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/MX/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/MX', parameters('privateDnsZoneName'), 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": [ + "MX" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed MX record." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed MX record." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/MX', parameters('privateDnsZoneName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed MX record." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "privateDnsZone_PTR": { + "copy": { + "name": "privateDnsZone_PTR", + "count": "[length(coalesce(parameters('ptr'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-PTRRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('ptr'), createArray())[copyIndex()].name]" + }, + "metadata": { + "value": "[tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'metadata')]" + }, + "ptrRecords": { + "value": "[tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'ptrRecords')]" + }, + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'ttl'), 3600)]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'roleAssignments')]" + } + }, + "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.32.4.45862", + "templateHash": "3080404733048745471" + }, + "name": "Private DNS Zone PTR record", + "description": "This module deploys a Private DNS Zone PTR record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", "properties": { - "type": "object", - "properties": { - "groupId": { - "type": "string", - "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." - } - }, - "memberName": { - "type": "string", - "metadata": { - "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to." - } - }, - "privateIPAddress": { - "type": "string", - "metadata": { - "description": "Required. A private IP address obtained from the private endpoint's subnet." - } + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." } }, - "metadata": { - "description": "Required. Properties of private endpoint IP configurations." + "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." + } } } }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." } }, - "_1.privateEndpointPrivateDnsZoneGroupType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the Private DNS Zone Group." - } - }, - "privateDnsZoneGroupConfigs": { - "type": "array", - "items": { - "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": "Required. The private DNS Zone Groups to associate the Private Endpoint. A DNS Zone Group can support up to 5 DNS zones." - } - } - }, + "name": { + "type": "string", "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } + "description": "Required. The name of the PTR record." } }, - "_1.secretSetOutputType": { + "metadata": { "type": "object", - "properties": { - "secretResourceId": { - "type": "string", - "metadata": { - "description": "The resourceId of the exported secret." - } - }, - "secretUri": { - "type": "string", - "metadata": { - "description": "The secret URI of the exported secret." - } - }, - "secretUriWithVersion": { - "type": "string", - "metadata": { - "description": "The secret URI with version of the exported secret." - } - } - }, + "nullable": true, "metadata": { - "description": "An AVM-aligned type for the output of the secret set via the secrets export feature.", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } + "description": "Optional. The metadata attached to the record set." } }, - "customerManagedKeyType": { - "type": "object", - "properties": { - "keyVaultResourceId": { - "type": "string", - "metadata": { - "description": "Required. The resource ID of a key vault to reference a customer managed key for encryption from." - } - }, - "keyName": { - "type": "string", - "metadata": { - "description": "Required. The name of the customer managed key to use for encryption." - } - }, - "keyVersion": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The version of the customer managed key to reference for encryption. If not provided, the deployment will use the latest version available at deployment time." - } - }, - "userAssignedIdentityResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use." - } - } - }, + "ptrRecords": { + "type": "array", + "nullable": true, "metadata": { - "description": "An AVM-aligned type for a customer-managed key. To be used if the resource type does not support auto-rotation of the customer-managed key.", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } + "description": "Optional. The list of PTR records in the record set." } }, - "diagnosticSettingFullType": { - "type": "object", + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "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')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "PTR": { + "type": "Microsoft.Network/privateDnsZones/PTR", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the diagnostic setting." - } - }, - "logCategoriesAndGroups": { - "type": "array", - "items": { - "type": "object", - "properties": { - "category": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." - } - }, - "categoryGroup": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." - } - }, - "enabled": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Enable or disable the category explicitly. Default is `true`." - } - } + "metadata": "[parameters('metadata')]", + "ptrRecords": "[parameters('ptrRecords')]", + "ttl": "[parameters('ttl')]" + } + }, + "PTR_roleAssignments": { + "copy": { + "name": "PTR_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/PTR/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/PTR', parameters('privateDnsZoneName'), 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": [ + "PTR" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed PTR record." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed PTR record." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/PTR', parameters('privateDnsZoneName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed PTR record." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "privateDnsZone_SOA": { + "copy": { + "name": "privateDnsZone_SOA", + "count": "[length(coalesce(parameters('soa'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-SOARecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('soa'), createArray())[copyIndex()].name]" + }, + "metadata": { + "value": "[tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'metadata')]" + }, + "soaRecord": { + "value": "[tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'soaRecord')]" + }, + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'ttl'), 3600)]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'roleAssignments')]" + } + }, + "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.32.4.45862", + "templateHash": "6653951445614700931" + }, + "name": "Private DNS Zone SOA record", + "description": "This module deploys a Private DNS Zone SOA record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "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." } }, - "nullable": true, - "metadata": { - "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." - } - }, - "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`." - } - } + "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'." } }, - "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." + "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." + } } } }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", "metadata": { - "description": "An AVM-aligned type for a diagnostic setting. To be used if both logs & metrics are supported by the resource provider.", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." } }, - "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." - } - } - }, + "name": { + "type": "string", "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.5.1" - } + "description": "Required. The name of the SOA record." } }, - "managedIdentityAllType": { + "metadata": { "type": "object", - "properties": { - "systemAssigned": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Enables system assigned managed identity on the resource." - } - }, - "userAssignedResourceIds": { - "type": "array", - "items": { - "type": "string" - }, - "nullable": true, - "metadata": { - "description": "Optional. The resource ID(s) to assign to the resource. Required if a user assigned identity is used for encryption." - } - } - }, + "nullable": true, "metadata": { - "description": "An AVM-aligned type for a managed identity configuration. To be used if both a system-assigned & user-assigned identities are supported by the resource provider.", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } + "description": "Optional. The metadata attached to the record set." } }, - "privateEndpointSingleServiceType": { + "soaRecord": { "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. A SOA record." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "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')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "SOA": { + "type": "Microsoft.Network/privateDnsZones/SOA", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the Private Endpoint." - } - }, - "location": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The location to deploy the Private Endpoint to." - } - }, - "privateLinkServiceConnectionName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the private link connection to create." - } - }, - "service": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The subresource to deploy the Private Endpoint for. For example \"vault\" for a Key Vault Private Endpoint." - } - }, - "subnetResourceId": { - "type": "string", - "metadata": { - "description": "Required. Resource ID of the subnet where the endpoint needs to be created." - } - }, - "resourceGroupResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The resource ID of the Resource Group the Private Endpoint will be created in. If not specified, the Resource Group of the provided Virtual Network Subnet is used." - } - }, - "privateDnsZoneGroup": { - "$ref": "#/definitions/_1.privateEndpointPrivateDnsZoneGroupType", - "nullable": true, - "metadata": { - "description": "Optional. The private DNS Zone Group to configure for the Private Endpoint." - } - }, - "isManualConnection": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. If Manual Private Link Connection is required." - } - }, - "manualConnectionRequestMessage": { - "type": "string", - "nullable": true, - "maxLength": 140, - "metadata": { - "description": "Optional. A message passed to the owner of the remote resource with the manual connection request." - } - }, - "customDnsConfigs": { - "type": "array", - "items": { - "$ref": "#/definitions/_1.privateEndpointCustomDnsConfigType" - }, - "nullable": true, - "metadata": { - "description": "Optional. Custom DNS configurations." - } - }, - "ipConfigurations": { - "type": "array", - "items": { - "$ref": "#/definitions/_1.privateEndpointIpConfigurationType" - }, - "nullable": true, - "metadata": { - "description": "Optional. A list of IP configurations of the Private Endpoint. This will be used to map to the first-party Service endpoints." - } - }, - "applicationSecurityGroupResourceIds": { - "type": "array", - "items": { - "type": "string" - }, - "nullable": true, - "metadata": { - "description": "Optional. Application security groups in which the Private Endpoint IP configuration is included." - } - }, - "customNetworkInterfaceName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The custom name of the network interface attached to the Private Endpoint." - } - }, - "lock": { - "$ref": "#/definitions/lockType", - "nullable": true, - "metadata": { - "description": "Optional. Specify the type of lock." - } - }, - "roleAssignments": { - "type": "array", - "items": { - "$ref": "#/definitions/roleAssignmentType" - }, - "nullable": true, - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "tags": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Tags to be applied on all resources/Resource Groups in this deployment." - } - }, - "enableTelemetry": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Enable/Disable usage telemetry for module." - } - } - }, - "metadata": { - "description": "An AVM-aligned type for a private endpoint. To be used if the private endpoint's default service / groupId can be assumed (i.e., for services that only have one Private Endpoint type like 'vault' for key vault).", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } + "metadata": "[parameters('metadata')]", + "soaRecord": "[parameters('soaRecord')]", + "ttl": "[parameters('ttl')]" } }, - "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." - } - } + "SOA_roleAssignments": { + "copy": { + "name": "SOA_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, - "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.5.1" - } - } - }, - "secretsOutputType": { - "type": "object", - "properties": {}, - "additionalProperties": { - "$ref": "#/definitions/_1.secretSetOutputType", - "metadata": { - "description": "An exported secret's references." - } + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/SOA/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/SOA', parameters('privateDnsZoneName'), 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')]" }, - "metadata": { - "description": "A map of the exported secrets", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } - } + "dependsOn": [ + "SOA" + ] } }, - "parameters": { + "outputs": { "name": { "type": "string", "metadata": { - "description": "Required. The name of Cognitive Services account." - } - }, - "kind": { - "type": "string", - "allowedValues": [ - "AIServices", - "AnomalyDetector", - "CognitiveServices", - "ComputerVision", - "ContentModerator", - "ContentSafety", - "ConversationalLanguageUnderstanding", - "CustomVision.Prediction", - "CustomVision.Training", - "Face", - "FormRecognizer", - "HealthInsights", - "ImmersiveReader", - "Internal.AllInOne", - "LUIS", - "LUIS.Authoring", - "LanguageAuthoring", - "MetricsAdvisor", - "OpenAI", - "Personalizer", - "QnAMaker.v2", - "SpeechServices", - "TextAnalytics", - "TextTranslation" - ], - "metadata": { - "description": "Required. Kind of the Cognitive Services account. Use 'Get-AzCognitiveServicesAccountSku' to determine a valid combinations of 'kind' and 'SKU' for your Azure region." - } + "description": "The name of the deployed SOA record." + }, + "value": "[parameters('name')]" }, - "sku": { + "resourceId": { "type": "string", - "defaultValue": "S0", - "allowedValues": [ - "C2", - "C3", - "C4", - "F0", - "F1", - "S", - "S0", - "S1", - "S10", - "S2", - "S3", - "S4", - "S5", - "S6", - "S7", - "S8", - "S9" - ], "metadata": { - "description": "Optional. SKU of the Cognitive Services account. Use 'Get-AzCognitiveServicesAccountSku' to determine a valid combinations of 'kind' and 'SKU' for your Azure region." - } + "description": "The resource ID of the deployed SOA record." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/SOA', parameters('privateDnsZoneName'), parameters('name'))]" }, - "location": { + "resourceGroupName": { "type": "string", - "defaultValue": "[resourceGroup().location]", "metadata": { - "description": "Optional. Location for all Resources." - } + "description": "The resource group of the deployed SOA record." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "privateDnsZone_SRV": { + "copy": { + "name": "privateDnsZone_SRV", + "count": "[length(coalesce(parameters('srv'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-SRVRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('srv'), createArray())[copyIndex()].name]" + }, + "metadata": { + "value": "[tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'metadata')]" + }, + "srvRecords": { + "value": "[tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'srvRecords')]" + }, + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'ttl'), 3600)]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'roleAssignments')]" + } + }, + "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.32.4.45862", + "templateHash": "5790774778713328446" }, - "diagnosticSettings": { + "name": "Private DNS Zone SRV record", + "description": "This module deploys a Private DNS Zone SRV record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { "type": "array", "items": { - "$ref": "#/definitions/diagnosticSettingFullType" + "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." + } + } + } }, - "nullable": true, - "metadata": { - "description": "Optional. The diagnostic settings of the service." - } - }, - "publicNetworkAccess": { + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { "type": "string", - "nullable": true, - "allowedValues": [ - "Enabled", - "Disabled" - ], "metadata": { - "description": "Optional. Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set and networkAcls are not set." + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." } }, - "customSubDomainName": { + "name": { "type": "string", - "nullable": true, "metadata": { - "description": "Conditional. Subdomain name used for token-based authentication. Required if 'networkAcls' or 'privateEndpoints' are set." + "description": "Required. The name of the SRV record." } }, - "networkAcls": { + "metadata": { "type": "object", "nullable": true, "metadata": { - "description": "Optional. A collection of rules governing the accessibility from specific network locations." + "description": "Optional. The metadata attached to the record set." } }, - "privateEndpoints": { + "srvRecords": { "type": "array", - "items": { - "$ref": "#/definitions/privateEndpointSingleServiceType" - }, "nullable": true, "metadata": { - "description": "Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible." + "description": "Optional. The list of SRV records in the record set." } }, - "lock": { - "$ref": "#/definitions/lockType", - "nullable": true, + "ttl": { + "type": "int", + "defaultValue": 3600, "metadata": { - "description": "Optional. The lock settings of the service." + "description": "Optional. The TTL (time-to-live) of the records in the record set." } }, "roleAssignments": { - "type": "array", - "items": { - "$ref": "#/definitions/roleAssignmentType" - }, - "nullable": true, + "$ref": "#/definitions/roleAssignmentType", "metadata": { "description": "Optional. Array of role assignments to create." } - }, - "tags": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Tags of the resource." + } + }, + "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')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" }, - "allowedFqdnList": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. List of allowed FQDN." - } - }, - "apiProperties": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The API properties for special APIs." - } - }, - "disableLocalAuth": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Optional. Allow only Azure AD authentication. Should be enabled for security reasons." - } - }, - "customerManagedKey": { - "$ref": "#/definitions/customerManagedKeyType", - "nullable": true, - "metadata": { - "description": "Optional. The customer managed key definition." + "SRV": { + "type": "Microsoft.Network/privateDnsZones/SRV", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "properties": { + "metadata": "[parameters('metadata')]", + "srvRecords": "[parameters('srvRecords')]", + "ttl": "[parameters('ttl')]" } }, - "dynamicThrottlingEnabled": { - "type": "bool", - "defaultValue": false, + "SRV_roleAssignments": { + "copy": { + "name": "SRV_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/SRV/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/SRV', parameters('privateDnsZoneName'), 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": [ + "SRV" + ] + } + }, + "outputs": { + "name": { + "type": "string", "metadata": { - "description": "Optional. The flag to enable dynamic throttling." - } + "description": "The name of the deployed SRV record." + }, + "value": "[parameters('name')]" }, - "migrationToken": { - "type": "securestring", - "nullable": true, + "resourceId": { + "type": "string", "metadata": { - "description": "Optional. Resource migration token." - } + "description": "The resource ID of the deployed SRV record." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/SRV', parameters('privateDnsZoneName'), parameters('name'))]" }, - "restore": { - "type": "bool", - "defaultValue": false, + "resourceGroupName": { + "type": "string", "metadata": { - "description": "Optional. Restore a soft-deleted cognitive service at deployment time. Will fail if no such soft-deleted resource exists." - } + "description": "The resource group of the deployed SRV record." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "privateDnsZone_TXT": { + "copy": { + "name": "privateDnsZone_TXT", + "count": "[length(coalesce(parameters('txt'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-TXTRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('txt'), createArray())[copyIndex()].name]" + }, + "metadata": { + "value": "[tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'metadata')]" + }, + "txtRecords": { + "value": "[tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'txtRecords')]" + }, + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'ttl'), 3600)]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'roleAssignments')]" + } + }, + "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.32.4.45862", + "templateHash": "1855369119498044639" }, - "restrictOutboundNetworkAccess": { - "type": "bool", - "defaultValue": true, + "name": "Private DNS Zone TXT record", + "description": "This module deploys a Private DNS Zone TXT record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "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." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", "metadata": { - "description": "Optional. Restrict outbound network access." + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." } }, - "userOwnedStorage": { - "type": "array", - "nullable": true, + "name": { + "type": "string", "metadata": { - "description": "Optional. The storage accounts for this resource." + "description": "Required. The name of the TXT record." } }, - "managedIdentities": { - "$ref": "#/definitions/managedIdentityAllType", + "metadata": { + "type": "object", "nullable": true, "metadata": { - "description": "Optional. The managed identity definition for this resource." + "description": "Optional. The metadata attached to the record set." } }, - "enableTelemetry": { - "type": "bool", - "defaultValue": true, + "ttl": { + "type": "int", + "defaultValue": 3600, "metadata": { - "description": "Optional. Enable/Disable usage telemetry for module." + "description": "Optional. The TTL (time-to-live) of the records in the record set." } }, - "deployments": { + "txtRecords": { "type": "array", - "items": { - "$ref": "#/definitions/deploymentType" - }, - "nullable": true, - "metadata": { - "description": "Optional. Array of deployments about cognitive service accounts to create." - } - }, - "secretsExportConfiguration": { - "$ref": "#/definitions/secretsExportConfigurationType", "nullable": true, "metadata": { - "description": "Optional. Key vault reference and secret settings for the module's secrets export." + "description": "Optional. The list of TXT records in the record set." } }, - "allowProjectManagement": { - "type": "bool", - "nullable": true, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", "metadata": { - "description": "Optional. Enable/Disable project management feature for AI Foundry." + "description": "Optional. Array of role assignments to create." } } }, @@ -48954,36 +38405,10 @@ "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)))))]" } ], - "enableReferencedModulesTelemetry": false, - "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", - "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned, UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', null())), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", "builtInRoleNames": { - "Cognitive Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '25fbc0a9-bd7c-42a3-aa1a-3b75d497ee68')]", - "Cognitive Services Custom Vision Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c1ff6cc2-c111-46fe-8896-e0ef812ad9f3')]", - "Cognitive Services Custom Vision Deployment": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5c4089e1-6d96-4d2f-b296-c1bc7137275f')]", - "Cognitive Services Custom Vision Labeler": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '88424f51-ebe7-446f-bc41-7fa16989e96c')]", - "Cognitive Services Custom Vision Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '93586559-c37d-4a6b-ba08-b9f0940c2d73')]", - "Cognitive Services Custom Vision Trainer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0a5ae4ab-0d65-4eeb-be61-29fc9b54394b')]", - "Cognitive Services Data Reader (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b59867f0-fa02-499b-be73-45a86b5b3e1c')]", - "Cognitive Services Face Recognizer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9894cab4-e18a-44aa-828b-cb588cd6f2d7')]", - "Cognitive Services Immersive Reader User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b2de6794-95db-4659-8781-7e080d3f2b9d')]", - "Cognitive Services Language Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f07febfe-79bc-46b1-8b37-790e26e6e498')]", - "Cognitive Services Language Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7628b7b8-a8b2-4cdc-b46f-e9b35248918e')]", - "Cognitive Services Language Writer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f2310ca1-dc64-4889-bb49-c8e0fa3d47a8')]", - "Cognitive Services LUIS Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f72c8140-2111-481c-87ff-72b910f6e3f8')]", - "Cognitive Services LUIS Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18e81cdc-4e98-4e29-a639-e7d10c5a6226')]", - "Cognitive Services LUIS Writer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6322a993-d5c9-4bed-b113-e49bbea25b27')]", - "Cognitive Services Metrics Advisor Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'cb43c632-a144-4ec5-977c-e80c4affc34a')]", - "Cognitive Services Metrics Advisor User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3b20f47b-3825-43cb-8114-4bd2201156a8')]", - "Cognitive Services OpenAI Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a001fd3d-188f-4b5d-821b-7da978bf7442')]", - "Cognitive Services OpenAI User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e0bd9bd-7b93-4f28-af87-19fc36ad61bd')]", - "Cognitive Services QnA Maker Editor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f4cc2bf9-21be-47a1-bdf1-5c5804381025')]", - "Cognitive Services QnA Maker Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '466ccd10-b268-4a11-b098-b4849f024126')]", - "Cognitive Services Speech Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0e75ca1e-0464-4b4d-8b93-68208a576181')]", - "Cognitive Services Speech User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f2dc8367-1007-4938-bd23-fe263f013447')]", - "Cognitive Services User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a97b65f3-24c7-4388-baec-2e87135dc908')]", - "Azure AI Developer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '64702f94-c441-49e6-a78b-ef80e0188fee')]", "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", @@ -48991,169 +38416,31 @@ } }, "resources": { - "cMKKeyVault::cMKKey": { - "condition": "[and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), not(empty(tryGet(parameters('customerManagedKey'), 'keyName')))))]", + "privateDnsZone": { "existing": true, - "type": "Microsoft.KeyVault/vaults/keys", - "apiVersion": "2023-07-01", - "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[2]]", - "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[4]]", - "name": "[format('{0}/{1}', last(split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')), tryGet(parameters('customerManagedKey'), 'keyName'))]" + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" }, - "avmTelemetry": { - "condition": "[parameters('enableTelemetry')]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2024-03-01", - "name": "[format('46d3xbcp.res.cognitiveservices-account.{0}.{1}', replace('0.11.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "TXT": { + "type": "Microsoft.Network/privateDnsZones/TXT", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", "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" - } - } - } + "metadata": "[parameters('metadata')]", + "ttl": "[parameters('ttl')]", + "txtRecords": "[parameters('txtRecords')]" } }, - "cMKKeyVault": { - "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId')))]", - "existing": true, - "type": "Microsoft.KeyVault/vaults", - "apiVersion": "2023-07-01", - "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[2]]", - "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[4]]", - "name": "[last(split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/'))]" - }, - "cMKUserAssignedIdentity": { - "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId')))]", - "existing": true, - "type": "Microsoft.ManagedIdentity/userAssignedIdentities", - "apiVersion": "2025-01-31-preview", - "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/')[2]]", - "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/')[4]]", - "name": "[last(split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/'))]" - }, - "cognitiveService": { - "type": "Microsoft.CognitiveServices/accounts", - "apiVersion": "2025-04-01-preview", - "name": "[parameters('name')]", - "kind": "[parameters('kind')]", - "identity": "[variables('identity')]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", - "sku": { - "name": "[parameters('sku')]" - }, - "properties": { - "allowProjectManagement": "[parameters('allowProjectManagement')]", - "customSubDomainName": "[parameters('customSubDomainName')]", - "networkAcls": "[if(not(empty(coalesce(parameters('networkAcls'), createObject()))), createObject('defaultAction', tryGet(parameters('networkAcls'), 'defaultAction'), 'virtualNetworkRules', coalesce(tryGet(parameters('networkAcls'), 'virtualNetworkRules'), createArray()), 'ipRules', coalesce(tryGet(parameters('networkAcls'), 'ipRules'), createArray())), null())]", - "publicNetworkAccess": "[if(not(equals(parameters('publicNetworkAccess'), null())), parameters('publicNetworkAccess'), if(not(empty(parameters('networkAcls'))), 'Enabled', 'Disabled'))]", - "allowedFqdnList": "[parameters('allowedFqdnList')]", - "apiProperties": "[parameters('apiProperties')]", - "disableLocalAuth": "[parameters('disableLocalAuth')]", - "encryption": "[if(not(empty(parameters('customerManagedKey'))), createObject('keySource', 'Microsoft.KeyVault', 'keyVaultProperties', createObject('identityClientId', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), ''))), reference('cMKUserAssignedIdentity').clientId, null()), 'keyVaultUri', reference('cMKKeyVault').vaultUri, 'keyName', parameters('customerManagedKey').keyName, 'keyVersion', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'keyVersion'), ''))), tryGet(parameters('customerManagedKey'), 'keyVersion'), last(split(reference('cMKKeyVault::cMKKey').keyUriWithVersion, '/'))))), null())]", - "migrationToken": "[parameters('migrationToken')]", - "restore": "[parameters('restore')]", - "restrictOutboundNetworkAccess": "[parameters('restrictOutboundNetworkAccess')]", - "userOwnedStorage": "[parameters('userOwnedStorage')]", - "dynamicThrottlingEnabled": "[parameters('dynamicThrottlingEnabled')]" - }, - "dependsOn": [ - "cMKKeyVault", - "cMKKeyVault::cMKKey", - "cMKUserAssignedIdentity" - ] - }, - "cognitiveService_deployments": { - "copy": { - "name": "cognitiveService_deployments", - "count": "[length(coalesce(parameters('deployments'), createArray()))]", - "mode": "serial", - "batchSize": 1 - }, - "type": "Microsoft.CognitiveServices/accounts/deployments", - "apiVersion": "2025-04-01-preview", - "name": "[format('{0}/{1}', parameters('name'), coalesce(tryGet(coalesce(parameters('deployments'), createArray())[copyIndex()], 'name'), format('{0}-deployments', parameters('name'))))]", - "properties": { - "model": "[coalesce(parameters('deployments'), createArray())[copyIndex()].model]", - "raiPolicyName": "[tryGet(coalesce(parameters('deployments'), createArray())[copyIndex()], 'raiPolicyName')]", - "versionUpgradeOption": "[tryGet(coalesce(parameters('deployments'), createArray())[copyIndex()], 'versionUpgradeOption')]" - }, - "sku": "[coalesce(tryGet(coalesce(parameters('deployments'), createArray())[copyIndex()], 'sku'), createObject('name', parameters('sku'), 'capacity', tryGet(parameters('sku'), 'capacity'), 'tier', tryGet(parameters('sku'), 'tier'), 'size', tryGet(parameters('sku'), 'size'), 'family', tryGet(parameters('sku'), 'family')))]", - "dependsOn": [ - "cognitiveService" - ] - }, - "cognitiveService_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.CognitiveServices/accounts/{0}', parameters('name'))]", - "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", - "properties": { - "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", - "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" - }, - "dependsOn": [ - "cognitiveService" - ] - }, - "cognitiveService_diagnosticSettings": { - "copy": { - "name": "cognitiveService_diagnosticSettings", - "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" - }, - "type": "Microsoft.Insights/diagnosticSettings", - "apiVersion": "2021-05-01-preview", - "scope": "[format('Microsoft.CognitiveServices/accounts/{0}', parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", - "properties": { - "copy": [ - { - "name": "metrics", - "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", - "input": { - "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", - "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", - "timeGrain": null - } - }, - { - "name": "logs", - "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", - "input": { - "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", - "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", - "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" - } - } - ], - "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", - "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", - "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", - "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", - "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", - "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" - }, - "dependsOn": [ - "cognitiveService" - ] - }, - "cognitiveService_roleAssignments": { + "TXT_roleAssignments": { "copy": { - "name": "cognitiveService_roleAssignments", + "name": "TXT_roleAssignments", "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.CognitiveServices/accounts/{0}', parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/TXT/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/TXT', parameters('privateDnsZoneName'), 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]", @@ -49164,176 +38451,18882 @@ "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" }, "dependsOn": [ - "cognitiveService" + "TXT" ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed TXT record." + }, + "value": "[parameters('name')]" }, - "cognitiveService_privateEndpoints": { - "copy": { - "name": "cognitiveService_privateEndpoints", - "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]" + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed TXT record." }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-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]]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'name'), format('pep-{0}-{1}-{2}', last(split(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'account'), copyIndex()))]" - }, - "privateLinkServiceConnections": "[if(not(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true())), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'account'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'account')))))), createObject('value', null()))]", - "manualPrivateLinkServiceConnections": "[if(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true()), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'account'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'account')), 'requestMessage', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualConnectionRequestMessage'), 'Manual approval required.'))))), createObject('value', null()))]", - "subnetResourceId": { - "value": "[coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId]" - }, - "enableTelemetry": { - "value": "[variables('enableReferencedModulesTelemetry')]" - }, - "location": { - "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'location'), reference(split(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location)]" - }, - "lock": { - "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" - }, - "privateDnsZoneGroup": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroup')]" - }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'roleAssignments')]" - }, - "tags": { - "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" - }, - "customDnsConfigs": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customDnsConfigs')]" - }, - "ipConfigurations": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'ipConfigurations')]" - }, - "applicationSecurityGroupResourceIds": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'applicationSecurityGroupResourceIds')]" - }, - "customNetworkInterfaceName": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customNetworkInterfaceName')]" - } + "value": "[resourceId('Microsoft.Network/privateDnsZones/TXT', parameters('privateDnsZoneName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed TXT record." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "privateDnsZone_virtualNetworkLinks": { + "copy": { + "name": "privateDnsZone_virtualNetworkLinks", + "count": "[length(coalesce(parameters('virtualNetworkLinks'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-VirtualNetworkLink-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'name'), format('{0}-vnetlink', last(split(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()].virtualNetworkResourceId, '/'))))]" + }, + "virtualNetworkResourceId": { + "value": "[coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()].virtualNetworkResourceId]" + }, + "location": { + "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'location'), 'global')]" + }, + "registrationEnabled": { + "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'registrationEnabled'), false())]" + }, + "tags": { + "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + }, + "resolutionPolicy": { + "value": "[tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'resolutionPolicy')]" + } + }, + "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.32.4.45862", + "templateHash": "15326596012552051215" + }, + "name": "Private DNS Zone Virtual Network Link", + "description": "This module deploys a Private DNS Zone Virtual Network Link.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "defaultValue": "[format('{0}-vnetlink', last(split(parameters('virtualNetworkResourceId'), '/')))]", + "metadata": { + "description": "Optional. The name of the virtual network link." + } + }, + "location": { + "type": "string", + "defaultValue": "global", + "metadata": { + "description": "Optional. The location of the PrivateDNSZone. Should be global." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "registrationEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Is auto-registration of virtual machine records in the virtual network in the Private DNS zone enabled?." + } + }, + "virtualNetworkResourceId": { + "type": "string", + "metadata": { + "description": "Required. Link to another virtual network resource ID." + } + }, + "resolutionPolicy": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resolution policy on the virtual network link. Only applicable for virtual network links to privatelink zones, and for A,AAAA,CNAME queries. When set to `NxDomainRedirect`, Azure DNS resolver falls back to public resolution if private dns query resolution results in non-existent domain response. `Default` is configured as the default option." + } + } + }, + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "virtualNetworkLink": { + "type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks", + "apiVersion": "2024-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "registrationEnabled": "[parameters('registrationEnabled')]", + "virtualNetwork": { + "id": "[parameters('virtualNetworkResourceId')]" }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "languageVersion": "2.0", - "contentVersion": "1.0.0.0", + "resolutionPolicy": "[parameters('resolutionPolicy')]" + } + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed virtual network link." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed virtual network link." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/virtualNetworkLinks', parameters('privateDnsZoneName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed virtual network link." + }, + "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('virtualNetworkLink', '2024-06-01', 'full').location]" + } + } + } + }, + "dependsOn": [ + "privateDnsZone" + ] + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the private DNS zone was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the private DNS zone." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the private DNS zone." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones', parameters('name'))]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('privateDnsZone', '2020-06-01', 'full').location]" + } + } + } + } + }, + "openAiPrivateDnsZone": { + "condition": "[parameters('networkIsolation')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "private-dns-openai-deployment", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[format('privatelink.openai.{0}', if(equals(toLower(environment().name), 'azureusgovernment'), 'azure.us', 'azure.com'))]" + }, + "virtualNetworkLinks": { + "value": [ + { + "virtualNetworkResourceId": "[parameters('virtualNetworkResourceId')]" + } + ] + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "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.32.4.45862", + "templateHash": "83178825086050429" + }, + "name": "Private DNS Zones", + "description": "This module deploys a Private DNS zone.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "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." + } + } + } + }, + "nullable": true + }, + "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." + } + } + }, + "nullable": true + }, + "aType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "aRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "ipv4Address": { + "type": "string", "metadata": { - "_generator": { - "name": "bicep", - "version": "0.34.44.8038", - "templateHash": "12389807800450456797" - }, - "name": "Private Endpoints", - "description": "This module deploys a Private Endpoint." - }, - "definitions": { - "privateDnsZoneGroupType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the Private DNS Zone Group." - } - }, - "privateDnsZoneGroupConfigs": { - "type": "array", - "items": { - "$ref": "#/definitions/privateDnsZoneGroupConfigType" - }, - "metadata": { - "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." - } - } - }, - "metadata": { - "__bicep_export!": true - } - }, - "ipConfigurationType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the resource that is unique within a resource group." - } - }, - "properties": { - "type": "object", - "properties": { - "groupId": { - "type": "string", - "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." - } - }, - "memberName": { - "type": "string", - "metadata": { - "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." - } - }, - "privateIPAddress": { - "type": "string", - "metadata": { - "description": "Required. A private IP address obtained from the private endpoint's subnet." - } - } - }, - "metadata": { - "description": "Required. Properties of private endpoint IP configurations." - } - } - }, - "metadata": { - "__bicep_export!": true - } - }, - "privateLinkServiceConnectionType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the private link service connection." - } - }, - "properties": { - "type": "object", - "properties": { - "groupIds": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." - } - }, - "privateLinkServiceId": { - "type": "string", - "metadata": { - "description": "Required. The resource id of private link service." - } - }, - "requestMessage": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." - } - } + "description": "Required. The IPv4 address of this A record." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of A records in the record set." + } + } + } + }, + "nullable": true + }, + "aaaaType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "aaaaRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "ipv6Address": { + "type": "string", + "metadata": { + "description": "Required. The IPv6 address of this AAAA record." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of AAAA records in the record set." + } + } + } + }, + "nullable": true + }, + "cnameType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "cnameRecord": { + "type": "object", + "properties": { + "cname": { + "type": "string", + "metadata": { + "description": "Required. The canonical name of the CNAME record." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The CNAME record in the record set." + } + } + } + }, + "nullable": true + }, + "mxType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "mxRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "exchange": { + "type": "string", + "metadata": { + "description": "Required. The domain name of the mail host for this MX record." + } + }, + "preference": { + "type": "int", + "metadata": { + "description": "Required. The preference value for this MX record." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of MX records in the record set." + } + } + } + }, + "nullable": true + }, + "ptrType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "ptrRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "ptrdname": { + "type": "string", + "metadata": { + "description": "Required. The PTR target domain name for this PTR record." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of PTR records in the record set." + } + } + } + }, + "nullable": true + }, + "soaType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "soaRecord": { + "type": "object", + "properties": { + "email": { + "type": "string", + "metadata": { + "description": "Required. The email contact for this SOA record." + } + }, + "expireTime": { + "type": "int", + "metadata": { + "description": "Required. The expire time for this SOA record." + } + }, + "host": { + "type": "string", + "metadata": { + "description": "Required. The domain name of the authoritative name server for this SOA record." + } + }, + "minimumTtl": { + "type": "int", + "metadata": { + "description": "Required. The minimum value for this SOA record. By convention this is used to determine the negative caching duration." + } + }, + "refreshTime": { + "type": "int", + "metadata": { + "description": "Required. The refresh value for this SOA record." + } + }, + "retryTime": { + "type": "int", + "metadata": { + "description": "Required. The retry time for this SOA record." + } + }, + "serialNumber": { + "type": "int", + "metadata": { + "description": "Required. The serial number for this SOA record." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The SOA record in the record set." + } + } + } + }, + "nullable": true + }, + "srvType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "srvRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "priority": { + "type": "int", + "metadata": { + "description": "Required. The priority value for this SRV record." + } + }, + "weight": { + "type": "int", + "metadata": { + "description": "Required. The weight value for this SRV record." + } + }, + "port": { + "type": "int", + "metadata": { + "description": "Required. The port value for this SRV record." + } + }, + "target": { + "type": "string", + "metadata": { + "description": "Required. The target domain name for this SRV record." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of SRV records in the record set." + } + } + } + }, + "nullable": true + }, + "txtType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "txtRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "value": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The text value of this TXT record." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of TXT records in the record set." + } + } + } + }, + "nullable": true + }, + "virtualNetworkLinkType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "minLength": 1, + "maxLength": 80, + "metadata": { + "description": "Optional. The resource name." + } + }, + "virtualNetworkResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the virtual network to link." + } + }, + "location": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Azure Region where the resource lives." + } + }, + "registrationEnabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Is auto-registration of virtual machine records in the virtual network in the Private DNS zone enabled?." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Resource tags." + } + }, + "resolutionPolicy": { + "type": "string", + "allowedValues": [ + "Default", + "NxDomainRedirect" + ], + "nullable": true, + "metadata": { + "description": "Optional. The resolution type of the private-dns-zone fallback machanism." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Private DNS zone name." + } + }, + "a": { + "$ref": "#/definitions/aType", + "metadata": { + "description": "Optional. Array of A records." + } + }, + "aaaa": { + "$ref": "#/definitions/aaaaType", + "metadata": { + "description": "Optional. Array of AAAA records." + } + }, + "cname": { + "$ref": "#/definitions/cnameType", + "metadata": { + "description": "Optional. Array of CNAME records." + } + }, + "mx": { + "$ref": "#/definitions/mxType", + "metadata": { + "description": "Optional. Array of MX records." + } + }, + "ptr": { + "$ref": "#/definitions/ptrType", + "metadata": { + "description": "Optional. Array of PTR records." + } + }, + "soa": { + "$ref": "#/definitions/soaType", + "metadata": { + "description": "Optional. Array of SOA records." + } + }, + "srv": { + "$ref": "#/definitions/srvType", + "metadata": { + "description": "Optional. Array of SRV records." + } + }, + "txt": { + "$ref": "#/definitions/txtType", + "metadata": { + "description": "Optional. Array of TXT records." + } + }, + "virtualNetworkLinks": { + "$ref": "#/definitions/virtualNetworkLinkType", + "metadata": { + "description": "Optional. Array of custom objects describing vNet links of the DNS zone. Each object should contain properties 'virtualNetworkResourceId' and 'registrationEnabled'. The 'vnetResourceId' is a resource ID of a vNet to link, 'registrationEnabled' (bool) enables automatic DNS registration in the zone for the linked vNet." + } + }, + "location": { + "type": "string", + "defaultValue": "global", + "metadata": { + "description": "Optional. The location of the PrivateDNSZone. Should be global." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "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')]", + "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": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privatednszone.{0}.{1}', replace('0.7.0', '.', '-'), 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" + } + } + } + } + }, + "privateDnsZone": { + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]" + }, + "privateDnsZone_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/privateDnsZones/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "privateDnsZone_roleAssignments": { + "copy": { + "name": "privateDnsZone_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones', 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": [ + "privateDnsZone" + ] + }, + "privateDnsZone_A": { + "copy": { + "name": "privateDnsZone_A", + "count": "[length(coalesce(parameters('a'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-ARecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('a'), createArray())[copyIndex()].name]" + }, + "aRecords": { + "value": "[tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'aRecords')]" + }, + "metadata": { + "value": "[tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'metadata')]" + }, + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'ttl'), 3600)]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'roleAssignments')]" + } + }, + "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.32.4.45862", + "templateHash": "2531120132215940282" + }, + "name": "Private DNS Zone A record", + "description": "This module deploys a Private DNS Zone A record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "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." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the A record." + } + }, + "aRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of A records in the record set." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "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')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "A": { + "type": "Microsoft.Network/privateDnsZones/A", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "properties": { + "aRecords": "[parameters('aRecords')]", + "metadata": "[parameters('metadata')]", + "ttl": "[parameters('ttl')]" + } + }, + "A_roleAssignments": { + "copy": { + "name": "A_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/A/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/A', parameters('privateDnsZoneName'), 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": [ + "A" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed A record." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed A record." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/A', parameters('privateDnsZoneName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed A record." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "privateDnsZone_AAAA": { + "copy": { + "name": "privateDnsZone_AAAA", + "count": "[length(coalesce(parameters('aaaa'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-AAAARecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('aaaa'), createArray())[copyIndex()].name]" + }, + "aaaaRecords": { + "value": "[tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'aaaaRecords')]" + }, + "metadata": { + "value": "[tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'metadata')]" + }, + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'ttl'), 3600)]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'roleAssignments')]" + } + }, + "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.32.4.45862", + "templateHash": "16709340450244912125" + }, + "name": "Private DNS Zone AAAA record", + "description": "This module deploys a Private DNS Zone AAAA record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "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." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the AAAA record." + } + }, + "aaaaRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of AAAA records in the record set." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "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')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "AAAA": { + "type": "Microsoft.Network/privateDnsZones/AAAA", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "properties": { + "aaaaRecords": "[parameters('aaaaRecords')]", + "metadata": "[parameters('metadata')]", + "ttl": "[parameters('ttl')]" + } + }, + "AAAA_roleAssignments": { + "copy": { + "name": "AAAA_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/AAAA/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/AAAA', parameters('privateDnsZoneName'), 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": [ + "AAAA" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed AAAA record." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed AAAA record." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/AAAA', parameters('privateDnsZoneName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed AAAA record." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "privateDnsZone_CNAME": { + "copy": { + "name": "privateDnsZone_CNAME", + "count": "[length(coalesce(parameters('cname'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-CNAMERecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('cname'), createArray())[copyIndex()].name]" + }, + "cnameRecord": { + "value": "[tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'cnameRecord')]" + }, + "metadata": { + "value": "[tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'metadata')]" + }, + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'ttl'), 3600)]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'roleAssignments')]" + } + }, + "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.32.4.45862", + "templateHash": "9976020649752073181" + }, + "name": "Private DNS Zone CNAME record", + "description": "This module deploys a Private DNS Zone CNAME record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "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." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the CNAME record." + } + }, + "cnameRecord": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. A CNAME record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "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')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "CNAME": { + "type": "Microsoft.Network/privateDnsZones/CNAME", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "properties": { + "cnameRecord": "[parameters('cnameRecord')]", + "metadata": "[parameters('metadata')]", + "ttl": "[parameters('ttl')]" + } + }, + "CNAME_roleAssignments": { + "copy": { + "name": "CNAME_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/CNAME/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/CNAME', parameters('privateDnsZoneName'), 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": [ + "CNAME" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed CNAME record." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed CNAME record." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/CNAME', parameters('privateDnsZoneName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed CNAME record." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "privateDnsZone_MX": { + "copy": { + "name": "privateDnsZone_MX", + "count": "[length(coalesce(parameters('mx'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-MXRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('mx'), createArray())[copyIndex()].name]" + }, + "metadata": { + "value": "[tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'metadata')]" + }, + "mxRecords": { + "value": "[tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'mxRecords')]" + }, + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'ttl'), 3600)]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'roleAssignments')]" + } + }, + "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.32.4.45862", + "templateHash": "2520323624213076361" + }, + "name": "Private DNS Zone MX record", + "description": "This module deploys a Private DNS Zone MX record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "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." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the MX record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "mxRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of MX records in the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "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')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "MX": { + "type": "Microsoft.Network/privateDnsZones/MX", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "properties": { + "metadata": "[parameters('metadata')]", + "mxRecords": "[parameters('mxRecords')]", + "ttl": "[parameters('ttl')]" + } + }, + "MX_roleAssignments": { + "copy": { + "name": "MX_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/MX/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/MX', parameters('privateDnsZoneName'), 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": [ + "MX" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed MX record." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed MX record." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/MX', parameters('privateDnsZoneName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed MX record." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "privateDnsZone_PTR": { + "copy": { + "name": "privateDnsZone_PTR", + "count": "[length(coalesce(parameters('ptr'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-PTRRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('ptr'), createArray())[copyIndex()].name]" + }, + "metadata": { + "value": "[tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'metadata')]" + }, + "ptrRecords": { + "value": "[tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'ptrRecords')]" + }, + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'ttl'), 3600)]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'roleAssignments')]" + } + }, + "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.32.4.45862", + "templateHash": "3080404733048745471" + }, + "name": "Private DNS Zone PTR record", + "description": "This module deploys a Private DNS Zone PTR record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "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." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the PTR record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "ptrRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of PTR records in the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "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')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "PTR": { + "type": "Microsoft.Network/privateDnsZones/PTR", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "properties": { + "metadata": "[parameters('metadata')]", + "ptrRecords": "[parameters('ptrRecords')]", + "ttl": "[parameters('ttl')]" + } + }, + "PTR_roleAssignments": { + "copy": { + "name": "PTR_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/PTR/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/PTR', parameters('privateDnsZoneName'), 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": [ + "PTR" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed PTR record." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed PTR record." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/PTR', parameters('privateDnsZoneName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed PTR record." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "privateDnsZone_SOA": { + "copy": { + "name": "privateDnsZone_SOA", + "count": "[length(coalesce(parameters('soa'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-SOARecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('soa'), createArray())[copyIndex()].name]" + }, + "metadata": { + "value": "[tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'metadata')]" + }, + "soaRecord": { + "value": "[tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'soaRecord')]" + }, + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'ttl'), 3600)]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'roleAssignments')]" + } + }, + "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.32.4.45862", + "templateHash": "6653951445614700931" + }, + "name": "Private DNS Zone SOA record", + "description": "This module deploys a Private DNS Zone SOA record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "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." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the SOA record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "soaRecord": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. A SOA record." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "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')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "SOA": { + "type": "Microsoft.Network/privateDnsZones/SOA", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "properties": { + "metadata": "[parameters('metadata')]", + "soaRecord": "[parameters('soaRecord')]", + "ttl": "[parameters('ttl')]" + } + }, + "SOA_roleAssignments": { + "copy": { + "name": "SOA_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/SOA/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/SOA', parameters('privateDnsZoneName'), 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": [ + "SOA" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed SOA record." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed SOA record." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/SOA', parameters('privateDnsZoneName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed SOA record." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "privateDnsZone_SRV": { + "copy": { + "name": "privateDnsZone_SRV", + "count": "[length(coalesce(parameters('srv'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-SRVRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('srv'), createArray())[copyIndex()].name]" + }, + "metadata": { + "value": "[tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'metadata')]" + }, + "srvRecords": { + "value": "[tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'srvRecords')]" + }, + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'ttl'), 3600)]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'roleAssignments')]" + } + }, + "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.32.4.45862", + "templateHash": "5790774778713328446" + }, + "name": "Private DNS Zone SRV record", + "description": "This module deploys a Private DNS Zone SRV record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "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." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the SRV record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "srvRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of SRV records in the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "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')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "SRV": { + "type": "Microsoft.Network/privateDnsZones/SRV", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "properties": { + "metadata": "[parameters('metadata')]", + "srvRecords": "[parameters('srvRecords')]", + "ttl": "[parameters('ttl')]" + } + }, + "SRV_roleAssignments": { + "copy": { + "name": "SRV_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/SRV/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/SRV', parameters('privateDnsZoneName'), 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": [ + "SRV" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed SRV record." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed SRV record." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/SRV', parameters('privateDnsZoneName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed SRV record." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "privateDnsZone_TXT": { + "copy": { + "name": "privateDnsZone_TXT", + "count": "[length(coalesce(parameters('txt'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-TXTRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('txt'), createArray())[copyIndex()].name]" + }, + "metadata": { + "value": "[tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'metadata')]" + }, + "txtRecords": { + "value": "[tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'txtRecords')]" + }, + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'ttl'), 3600)]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'roleAssignments')]" + } + }, + "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.32.4.45862", + "templateHash": "1855369119498044639" + }, + "name": "Private DNS Zone TXT record", + "description": "This module deploys a Private DNS Zone TXT record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "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." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the TXT record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "txtRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of TXT records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "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')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "TXT": { + "type": "Microsoft.Network/privateDnsZones/TXT", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "properties": { + "metadata": "[parameters('metadata')]", + "ttl": "[parameters('ttl')]", + "txtRecords": "[parameters('txtRecords')]" + } + }, + "TXT_roleAssignments": { + "copy": { + "name": "TXT_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/TXT/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/TXT', parameters('privateDnsZoneName'), 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": [ + "TXT" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed TXT record." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed TXT record." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/TXT', parameters('privateDnsZoneName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed TXT record." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "privateDnsZone_virtualNetworkLinks": { + "copy": { + "name": "privateDnsZone_virtualNetworkLinks", + "count": "[length(coalesce(parameters('virtualNetworkLinks'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-VirtualNetworkLink-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'name'), format('{0}-vnetlink', last(split(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()].virtualNetworkResourceId, '/'))))]" + }, + "virtualNetworkResourceId": { + "value": "[coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()].virtualNetworkResourceId]" + }, + "location": { + "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'location'), 'global')]" + }, + "registrationEnabled": { + "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'registrationEnabled'), false())]" + }, + "tags": { + "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + }, + "resolutionPolicy": { + "value": "[tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'resolutionPolicy')]" + } + }, + "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.32.4.45862", + "templateHash": "15326596012552051215" + }, + "name": "Private DNS Zone Virtual Network Link", + "description": "This module deploys a Private DNS Zone Virtual Network Link.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "defaultValue": "[format('{0}-vnetlink', last(split(parameters('virtualNetworkResourceId'), '/')))]", + "metadata": { + "description": "Optional. The name of the virtual network link." + } + }, + "location": { + "type": "string", + "defaultValue": "global", + "metadata": { + "description": "Optional. The location of the PrivateDNSZone. Should be global." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "registrationEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Is auto-registration of virtual machine records in the virtual network in the Private DNS zone enabled?." + } + }, + "virtualNetworkResourceId": { + "type": "string", + "metadata": { + "description": "Required. Link to another virtual network resource ID." + } + }, + "resolutionPolicy": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resolution policy on the virtual network link. Only applicable for virtual network links to privatelink zones, and for A,AAAA,CNAME queries. When set to `NxDomainRedirect`, Azure DNS resolver falls back to public resolution if private dns query resolution results in non-existent domain response. `Default` is configured as the default option." + } + } + }, + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "virtualNetworkLink": { + "type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks", + "apiVersion": "2024-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "registrationEnabled": "[parameters('registrationEnabled')]", + "virtualNetwork": { + "id": "[parameters('virtualNetworkResourceId')]" + }, + "resolutionPolicy": "[parameters('resolutionPolicy')]" + } + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed virtual network link." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed virtual network link." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/virtualNetworkLinks', parameters('privateDnsZoneName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed virtual network link." + }, + "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('virtualNetworkLink', '2024-06-01', 'full').location]" + } + } + } + }, + "dependsOn": [ + "privateDnsZone" + ] + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the private DNS zone was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the private DNS zone." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the private DNS zone." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones', parameters('name'))]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('privateDnsZone', '2020-06-01', 'full').location]" + } + } + } + } + }, + "aiServices": { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('{0}-ai-services-deployment', parameters('name')), 64)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[format('cog{0}{1}', parameters('name'), parameters('resourceToken'))]" + }, + "location": { + "value": "[parameters('location')]" + }, + "kind": { + "value": "AIServices" + }, + "category": { + "value": "AIServices" + }, + "networkIsolation": { + "value": "[parameters('networkIsolation')]" + }, + "networkAcls": { + "value": "[parameters('networkAcls')]" + }, + "virtualNetworkSubnetResourceId": "[if(parameters('networkIsolation'), createObject('value', parameters('virtualNetworkSubnetResourceId')), createObject('value', ''))]", + "privateDnsZonesResourceIds": "[if(parameters('networkIsolation'), createObject('value', createArray(reference('cognitiveServicesPrivateDnsZone').outputs.resourceId.value, reference('openAiPrivateDnsZone').outputs.resourceId.value)), createObject('value', createArray()))]", + "logAnalyticsWorkspaceResourceId": { + "value": "[parameters('logAnalyticsWorkspaceResourceId')]" + }, + "aiModelDeployments": { + "value": "[parameters('aiModelDeployments')]" + }, + "roleAssignments": { + "value": "[variables('allRoleAssignments')]" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "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.1.42791", + "templateHash": "2348080591288311162" + } + }, + "definitions": { + "deploymentsType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of cognitive service account deployment." + } + }, + "model": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of Cognitive Services account deployment model." + } + }, + "format": { + "type": "string", + "metadata": { + "description": "Required. The format of Cognitive Services account deployment model." + } + }, + "version": { + "type": "string", + "metadata": { + "description": "Required. The version of Cognitive Services account deployment model." + } + } + }, + "metadata": { + "description": "Required. Properties of Cognitive Services account deployment model." + } + }, + "sku": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource model definition representing SKU." + } + }, + "capacity": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The capacity of the resource model definition representing SKU." + } + }, + "tier": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The tier of the resource model definition representing SKU." + } + }, + "size": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The size of the resource model definition representing SKU." + } + }, + "family": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The family of the resource model definition representing SKU." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The resource model definition representing SKU." + } + }, + "raiPolicyName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of RAI policy." + } + }, + "versionUpgradeOption": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The version upgrade option." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "../customTypes.bicep" + } + } + }, + "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.5.1" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Name of the Cognitive Services resource. Must be unique in the resource group." + } + }, + "location": { + "type": "string", + "metadata": { + "description": "The location of the Cognitive Services resource." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "AIServices", + "AnomalyDetector", + "CognitiveServices", + "ComputerVision", + "ContentModerator", + "ContentSafety", + "ConversationalLanguageUnderstanding", + "CustomVision.Prediction", + "CustomVision.Training", + "Face", + "FormRecognizer", + "HealthInsights", + "ImmersiveReader", + "Internal.AllInOne", + "LUIS", + "LUIS.Authoring", + "LanguageAuthoring", + "MetricsAdvisor", + "OpenAI", + "Personalizer", + "QnAMaker.v2", + "SpeechServices", + "TextAnalytics", + "TextTranslation" + ], + "metadata": { + "description": "Required. Kind of the Cognitive Services account. Use 'Get-AzCognitiveServicesAccountSku' to determine a valid combinations of 'kind' and 'SKU' for your Azure region." + } + }, + "sku": { + "type": "string", + "defaultValue": "S0", + "allowedValues": [ + "S", + "S0", + "S1", + "S2", + "S3", + "S4", + "S5", + "S6", + "S7", + "S8" + ], + "metadata": { + "description": "Required. The SKU of the Cognitive Services account. Use 'Get-AzCognitiveServicesAccountSku' to determine a valid combinations of 'kind' and 'SKU' for your Azure region." + } + }, + "category": { + "type": "string", + "defaultValue": "CognitiveService", + "metadata": { + "description": "Category of the Cognitive Services account." + } + }, + "networkIsolation": { + "type": "bool", + "metadata": { + "description": "Specifies whether to enable network isolation. If true, the resource will be deployed in a private endpoint and public network access will be disabled." + } + }, + "privateDnsZonesResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "defaultValue": [], + "metadata": { + "description": "Existing resource ID of the private DNS zone for the private endpoint." + } + }, + "virtualNetworkSubnetResourceId": { + "type": "string", + "metadata": { + "description": "Resource ID of the subnet for the private endpoint." + } + }, + "logAnalyticsWorkspaceResourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the Log Analytics workspace to use for diagnostic settings." + } + }, + "aiModelDeployments": { + "type": "array", + "items": { + "$ref": "#/definitions/deploymentsType" + }, + "defaultValue": [], + "metadata": { + "description": "Optional. Specifies the OpenAI deployments to create." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Tags to be applied to the resources." + } + }, + "networkAcls": { + "type": "object", + "metadata": { + "description": "Optional. A collection of rules governing the accessibility from specific network locations." + } + } + }, + "variables": { + "copy": [ + { + "name": "privateDnsZones", + "count": "[length(parameters('privateDnsZonesResourceIds'))]", + "input": { + "privateDnsZoneResourceId": "[parameters('privateDnsZonesResourceIds')[copyIndex('privateDnsZones')]]" + } + } + ], + "nameFormatted": "[take(toLower(parameters('name')), 24)]" + }, + "resources": { + "cognitiveService": { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('cog-{0}-{1}-deployment', parameters('kind'), parameters('name')), 64)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[variables('nameFormatted')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "sku": { + "value": "[parameters('sku')]" + }, + "kind": { + "value": "[parameters('kind')]" + }, + "allowProjectManagement": { + "value": true + }, + "managedIdentities": { + "value": { + "systemAssigned": true + } + }, + "deployments": { + "value": "[parameters('aiModelDeployments')]" + }, + "customSubDomainName": { + "value": "[parameters('name')]" + }, + "disableLocalAuth": { + "value": "[parameters('networkIsolation')]" + }, + "publicNetworkAccess": "[if(parameters('networkIsolation'), createObject('value', 'Disabled'), createObject('value', 'Enabled'))]", + "diagnosticSettings": { + "value": [ + { + "workspaceResourceId": "[parameters('logAnalyticsWorkspaceResourceId')]" + } + ] + }, + "roleAssignments": { + "value": "[parameters('roleAssignments')]" + }, + "networkAcls": { + "value": "[parameters('networkAcls')]" + }, + "privateEndpoints": "[if(parameters('networkIsolation'), createObject('value', createArray(createObject('privateDnsZoneGroup', createObject('privateDnsZoneGroupConfigs', variables('privateDnsZones')), 'subnetResourceId', parameters('virtualNetworkSubnetResourceId')))), createObject('value', createArray()))]" + }, + "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.1.42791", + "templateHash": "16135659971302525380" + }, + "name": "Cognitive Services", + "description": "This module deploys a Cognitive Service." + }, + "definitions": { + "privateEndpointOutputType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the private endpoint." + } + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the private endpoint." + } + }, + "groupId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "The group Id for the private endpoint Group." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "FQDN that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "A list of private IP addresses of the private endpoint." + } + } + } + }, + "metadata": { + "description": "The custom DNS configurations of the private endpoint." + } + }, + "networkInterfaceResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "The IDs of the network interfaces associated with the private endpoint." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for the private endpoint output." + } + }, + "deploymentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of cognitive service account deployment." + } + }, + "model": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of Cognitive Services account deployment model." + } + }, + "format": { + "type": "string", + "metadata": { + "description": "Required. The format of Cognitive Services account deployment model." + } + }, + "version": { + "type": "string", + "metadata": { + "description": "Required. The version of Cognitive Services account deployment model." + } + } + }, + "metadata": { + "description": "Required. Properties of Cognitive Services account deployment model." + } + }, + "sku": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource model definition representing SKU." + } + }, + "capacity": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The capacity of the resource model definition representing SKU." + } + }, + "tier": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The tier of the resource model definition representing SKU." + } + }, + "size": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The size of the resource model definition representing SKU." + } + }, + "family": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The family of the resource model definition representing SKU." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The resource model definition representing SKU." + } + }, + "raiPolicyName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of RAI policy." + } + }, + "versionUpgradeOption": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The version upgrade option." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for a cognitive services account deployment." + } + }, + "endpointType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Type of the endpoint." + } + }, + "endpoint": { + "type": "string", + "nullable": true, + "metadata": { + "description": "The endpoint URI." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for a cognitive services account endpoint." + } + }, + "secretsExportConfigurationType": { + "type": "object", + "properties": { + "keyVaultResourceId": { + "type": "string", + "metadata": { + "description": "Required. The key vault name where to store the keys and connection strings generated by the modules." + } + }, + "accessKey1Name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name for the accessKey1 secret to create." + } + }, + "accessKey2Name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name for the accessKey2 secret to create." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type of the secrets exported to the provided Key Vault." + } + }, + "_1.privateEndpointCustomDnsConfigType": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. FQDN that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "_1.privateEndpointIpConfigurationType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource that is unique within a resource group." + } + }, + "properties": { + "type": "object", + "properties": { + "groupId": { + "type": "string", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } + } + }, + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "_1.privateEndpointPrivateDnsZoneGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "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": "Required. The private DNS Zone Groups to associate the Private Endpoint. A DNS Zone Group can support up to 5 DNS zones." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "_1.secretSetOutputType": { + "type": "object", + "properties": { + "secretResourceId": { + "type": "string", + "metadata": { + "description": "The resourceId of the exported secret." + } + }, + "secretUri": { + "type": "string", + "metadata": { + "description": "The secret URI of the exported secret." + } + }, + "secretUriWithVersion": { + "type": "string", + "metadata": { + "description": "The secret URI with version of the exported secret." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for the output of the secret set via the secrets export feature.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "customerManagedKeyType": { + "type": "object", + "properties": { + "keyVaultResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of a key vault to reference a customer managed key for encryption from." + } + }, + "keyName": { + "type": "string", + "metadata": { + "description": "Required. The name of the customer managed key to use for encryption." + } + }, + "keyVersion": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The version of the customer managed key to reference for encryption. If not provided, the deployment will use the latest version available at deployment time." + } + }, + "userAssignedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a customer-managed key. To be used if the resource type does not support auto-rotation of the customer-managed key.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "diagnosticSettingFullType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "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 logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "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 both logs & metrics are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "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." + } + } + }, + "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.5.1" + } + } + }, + "managedIdentityAllType": { + "type": "object", + "properties": { + "systemAssigned": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enables system assigned managed identity on the resource." + } + }, + "userAssignedResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The resource ID(s) to assign to the resource. Required if a user assigned identity is used for encryption." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a managed identity configuration. To be used if both a system-assigned & user-assigned identities are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "privateEndpointSingleServiceType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private Endpoint." + } + }, + "location": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The location to deploy the Private Endpoint to." + } + }, + "privateLinkServiceConnectionName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private link connection to create." + } + }, + "service": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The subresource to deploy the Private Endpoint for. For example \"vault\" for a Key Vault Private Endpoint." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + } + }, + "resourceGroupResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the Resource Group the Private Endpoint will be created in. If not specified, the Resource Group of the provided Virtual Network Subnet is used." + } + }, + "privateDnsZoneGroup": { + "$ref": "#/definitions/_1.privateEndpointPrivateDnsZoneGroupType", + "nullable": true, + "metadata": { + "description": "Optional. The private DNS Zone Group to configure for the Private Endpoint." + } + }, + "isManualConnection": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. If Manual Private Link Connection is required." + } + }, + "manualConnectionRequestMessage": { + "type": "string", + "nullable": true, + "maxLength": 140, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with the manual connection request." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/_1.privateEndpointCustomDnsConfigType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Custom DNS configurations." + } + }, + "ipConfigurations": { + "type": "array", + "items": { + "$ref": "#/definitions/_1.privateEndpointIpConfigurationType" + }, + "nullable": true, + "metadata": { + "description": "Optional. A list of IP configurations of the Private Endpoint. This will be used to map to the first-party Service endpoints." + } + }, + "applicationSecurityGroupResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Application security groups in which the Private Endpoint IP configuration is included." + } + }, + "customNetworkInterfaceName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The custom name of the network interface attached to the Private Endpoint." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags to be applied on all resources/Resource Groups in this deployment." + } + }, + "enableTelemetry": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a private endpoint. To be used if the private endpoint's default service / groupId can be assumed (i.e., for services that only have one Private Endpoint type like 'vault' for key vault).", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "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.5.1" + } + } + }, + "secretsOutputType": { + "type": "object", + "properties": {}, + "additionalProperties": { + "$ref": "#/definitions/_1.secretSetOutputType", + "metadata": { + "description": "An exported secret's references." + } + }, + "metadata": { + "description": "A map of the exported secrets", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of Cognitive Services account." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "AIServices", + "AnomalyDetector", + "CognitiveServices", + "ComputerVision", + "ContentModerator", + "ContentSafety", + "ConversationalLanguageUnderstanding", + "CustomVision.Prediction", + "CustomVision.Training", + "Face", + "FormRecognizer", + "HealthInsights", + "ImmersiveReader", + "Internal.AllInOne", + "LUIS", + "LUIS.Authoring", + "LanguageAuthoring", + "MetricsAdvisor", + "OpenAI", + "Personalizer", + "QnAMaker.v2", + "SpeechServices", + "TextAnalytics", + "TextTranslation" + ], + "metadata": { + "description": "Required. Kind of the Cognitive Services account. Use 'Get-AzCognitiveServicesAccountSku' to determine a valid combinations of 'kind' and 'SKU' for your Azure region." + } + }, + "sku": { + "type": "string", + "defaultValue": "S0", + "allowedValues": [ + "C2", + "C3", + "C4", + "F0", + "F1", + "S", + "S0", + "S1", + "S10", + "S2", + "S3", + "S4", + "S5", + "S6", + "S7", + "S8", + "S9" + ], + "metadata": { + "description": "Optional. SKU of the Cognitive Services account. Use 'Get-AzCognitiveServicesAccountSku' to determine a valid combinations of 'kind' and 'SKU' for your Azure region." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all Resources." + } + }, + "diagnosticSettings": { + "type": "array", + "items": { + "$ref": "#/definitions/diagnosticSettingFullType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "publicNetworkAccess": { + "type": "string", + "nullable": true, + "allowedValues": [ + "Enabled", + "Disabled" + ], + "metadata": { + "description": "Optional. Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set and networkAcls are not set." + } + }, + "customSubDomainName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. Subdomain name used for token-based authentication. Required if 'networkAcls' or 'privateEndpoints' are set." + } + }, + "networkAcls": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. A collection of rules governing the accessibility from specific network locations." + } + }, + "privateEndpoints": { + "type": "array", + "items": { + "$ref": "#/definitions/privateEndpointSingleServiceType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible." + } + }, + "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", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "allowedFqdnList": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. List of allowed FQDN." + } + }, + "apiProperties": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The API properties for special APIs." + } + }, + "disableLocalAuth": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Allow only Azure AD authentication. Should be enabled for security reasons." + } + }, + "customerManagedKey": { + "$ref": "#/definitions/customerManagedKeyType", + "nullable": true, + "metadata": { + "description": "Optional. The customer managed key definition." + } + }, + "dynamicThrottlingEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. The flag to enable dynamic throttling." + } + }, + "migrationToken": { + "type": "securestring", + "nullable": true, + "metadata": { + "description": "Optional. Resource migration token." + } + }, + "restore": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Restore a soft-deleted cognitive service at deployment time. Will fail if no such soft-deleted resource exists." + } + }, + "restrictOutboundNetworkAccess": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Restrict outbound network access." + } + }, + "userOwnedStorage": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The storage accounts for this resource." + } + }, + "managedIdentities": { + "$ref": "#/definitions/managedIdentityAllType", + "nullable": true, + "metadata": { + "description": "Optional. The managed identity definition for this resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "deployments": { + "type": "array", + "items": { + "$ref": "#/definitions/deploymentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of deployments about cognitive service accounts to create." + } + }, + "secretsExportConfiguration": { + "$ref": "#/definitions/secretsExportConfigurationType", + "nullable": true, + "metadata": { + "description": "Optional. Key vault reference and secret settings for the module's secrets export." + } + }, + "allowProjectManagement": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable/Disable project management feature for AI Foundry." + } + } + }, + "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)))))]" + } + ], + "enableReferencedModulesTelemetry": false, + "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", + "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned, UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', null())), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", + "builtInRoleNames": { + "Cognitive Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '25fbc0a9-bd7c-42a3-aa1a-3b75d497ee68')]", + "Cognitive Services Custom Vision Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c1ff6cc2-c111-46fe-8896-e0ef812ad9f3')]", + "Cognitive Services Custom Vision Deployment": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5c4089e1-6d96-4d2f-b296-c1bc7137275f')]", + "Cognitive Services Custom Vision Labeler": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '88424f51-ebe7-446f-bc41-7fa16989e96c')]", + "Cognitive Services Custom Vision Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '93586559-c37d-4a6b-ba08-b9f0940c2d73')]", + "Cognitive Services Custom Vision Trainer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0a5ae4ab-0d65-4eeb-be61-29fc9b54394b')]", + "Cognitive Services Data Reader (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b59867f0-fa02-499b-be73-45a86b5b3e1c')]", + "Cognitive Services Face Recognizer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9894cab4-e18a-44aa-828b-cb588cd6f2d7')]", + "Cognitive Services Immersive Reader User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b2de6794-95db-4659-8781-7e080d3f2b9d')]", + "Cognitive Services Language Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f07febfe-79bc-46b1-8b37-790e26e6e498')]", + "Cognitive Services Language Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7628b7b8-a8b2-4cdc-b46f-e9b35248918e')]", + "Cognitive Services Language Writer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f2310ca1-dc64-4889-bb49-c8e0fa3d47a8')]", + "Cognitive Services LUIS Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f72c8140-2111-481c-87ff-72b910f6e3f8')]", + "Cognitive Services LUIS Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18e81cdc-4e98-4e29-a639-e7d10c5a6226')]", + "Cognitive Services LUIS Writer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6322a993-d5c9-4bed-b113-e49bbea25b27')]", + "Cognitive Services Metrics Advisor Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'cb43c632-a144-4ec5-977c-e80c4affc34a')]", + "Cognitive Services Metrics Advisor User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3b20f47b-3825-43cb-8114-4bd2201156a8')]", + "Cognitive Services OpenAI Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a001fd3d-188f-4b5d-821b-7da978bf7442')]", + "Cognitive Services OpenAI User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e0bd9bd-7b93-4f28-af87-19fc36ad61bd')]", + "Cognitive Services QnA Maker Editor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f4cc2bf9-21be-47a1-bdf1-5c5804381025')]", + "Cognitive Services QnA Maker Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '466ccd10-b268-4a11-b098-b4849f024126')]", + "Cognitive Services Speech Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0e75ca1e-0464-4b4d-8b93-68208a576181')]", + "Cognitive Services Speech User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f2dc8367-1007-4938-bd23-fe263f013447')]", + "Cognitive Services User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a97b65f3-24c7-4388-baec-2e87135dc908')]", + "Azure AI Developer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '64702f94-c441-49e6-a78b-ef80e0188fee')]", + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "cMKKeyVault::cMKKey": { + "condition": "[and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), not(empty(tryGet(parameters('customerManagedKey'), 'keyName')))))]", + "existing": true, + "type": "Microsoft.KeyVault/vaults/keys", + "apiVersion": "2023-07-01", + "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[2]]", + "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[4]]", + "name": "[format('{0}/{1}', last(split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')), tryGet(parameters('customerManagedKey'), 'keyName'))]" + }, + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.cognitiveservices-account.{0}.{1}', replace('0.11.0', '.', '-'), 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" + } + } + } + } + }, + "cMKKeyVault": { + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId')))]", + "existing": true, + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2023-07-01", + "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[2]]", + "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[4]]", + "name": "[last(split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/'))]" + }, + "cMKUserAssignedIdentity": { + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId')))]", + "existing": true, + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2025-01-31-preview", + "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/')[2]]", + "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/')[4]]", + "name": "[last(split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/'))]" + }, + "cognitiveService": { + "type": "Microsoft.CognitiveServices/accounts", + "apiVersion": "2025-04-01-preview", + "name": "[parameters('name')]", + "kind": "[parameters('kind')]", + "identity": "[variables('identity')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "sku": { + "name": "[parameters('sku')]" + }, + "properties": { + "allowProjectManagement": "[parameters('allowProjectManagement')]", + "customSubDomainName": "[parameters('customSubDomainName')]", + "networkAcls": "[if(not(empty(coalesce(parameters('networkAcls'), createObject()))), createObject('defaultAction', tryGet(parameters('networkAcls'), 'defaultAction'), 'virtualNetworkRules', coalesce(tryGet(parameters('networkAcls'), 'virtualNetworkRules'), createArray()), 'ipRules', coalesce(tryGet(parameters('networkAcls'), 'ipRules'), createArray())), null())]", + "publicNetworkAccess": "[if(not(equals(parameters('publicNetworkAccess'), null())), parameters('publicNetworkAccess'), if(not(empty(parameters('networkAcls'))), 'Enabled', 'Disabled'))]", + "allowedFqdnList": "[parameters('allowedFqdnList')]", + "apiProperties": "[parameters('apiProperties')]", + "disableLocalAuth": "[parameters('disableLocalAuth')]", + "encryption": "[if(not(empty(parameters('customerManagedKey'))), createObject('keySource', 'Microsoft.KeyVault', 'keyVaultProperties', createObject('identityClientId', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), ''))), reference('cMKUserAssignedIdentity').clientId, null()), 'keyVaultUri', reference('cMKKeyVault').vaultUri, 'keyName', parameters('customerManagedKey').keyName, 'keyVersion', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'keyVersion'), ''))), tryGet(parameters('customerManagedKey'), 'keyVersion'), last(split(reference('cMKKeyVault::cMKKey').keyUriWithVersion, '/'))))), null())]", + "migrationToken": "[parameters('migrationToken')]", + "restore": "[parameters('restore')]", + "restrictOutboundNetworkAccess": "[parameters('restrictOutboundNetworkAccess')]", + "userOwnedStorage": "[parameters('userOwnedStorage')]", + "dynamicThrottlingEnabled": "[parameters('dynamicThrottlingEnabled')]" + }, + "dependsOn": [ + "cMKKeyVault", + "cMKKeyVault::cMKKey", + "cMKUserAssignedIdentity" + ] + }, + "cognitiveService_deployments": { + "copy": { + "name": "cognitiveService_deployments", + "count": "[length(coalesce(parameters('deployments'), createArray()))]", + "mode": "serial", + "batchSize": 1 + }, + "type": "Microsoft.CognitiveServices/accounts/deployments", + "apiVersion": "2025-04-01-preview", + "name": "[format('{0}/{1}', parameters('name'), coalesce(tryGet(coalesce(parameters('deployments'), createArray())[copyIndex()], 'name'), format('{0}-deployments', parameters('name'))))]", + "properties": { + "model": "[coalesce(parameters('deployments'), createArray())[copyIndex()].model]", + "raiPolicyName": "[tryGet(coalesce(parameters('deployments'), createArray())[copyIndex()], 'raiPolicyName')]", + "versionUpgradeOption": "[tryGet(coalesce(parameters('deployments'), createArray())[copyIndex()], 'versionUpgradeOption')]" + }, + "sku": "[coalesce(tryGet(coalesce(parameters('deployments'), createArray())[copyIndex()], 'sku'), createObject('name', parameters('sku'), 'capacity', tryGet(parameters('sku'), 'capacity'), 'tier', tryGet(parameters('sku'), 'tier'), 'size', tryGet(parameters('sku'), 'size'), 'family', tryGet(parameters('sku'), 'family')))]", + "dependsOn": [ + "cognitiveService" + ] + }, + "cognitiveService_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.CognitiveServices/accounts/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "cognitiveService" + ] + }, + "cognitiveService_diagnosticSettings": { + "copy": { + "name": "cognitiveService_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.CognitiveServices/accounts/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null + } + }, + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "cognitiveService" + ] + }, + "cognitiveService_roleAssignments": { + "copy": { + "name": "cognitiveService_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.CognitiveServices/accounts/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.CognitiveServices/accounts', 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": [ + "cognitiveService" + ] + }, + "cognitiveService_privateEndpoints": { + "copy": { + "name": "cognitiveService_privateEndpoints", + "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-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]]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'name'), format('pep-{0}-{1}-{2}', last(split(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'account'), copyIndex()))]" + }, + "privateLinkServiceConnections": "[if(not(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true())), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'account'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'account')))))), createObject('value', null()))]", + "manualPrivateLinkServiceConnections": "[if(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true()), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'account'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'account')), 'requestMessage', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualConnectionRequestMessage'), 'Manual approval required.'))))), createObject('value', null()))]", + "subnetResourceId": { + "value": "[coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId]" + }, + "enableTelemetry": { + "value": "[variables('enableReferencedModulesTelemetry')]" + }, + "location": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'location'), reference(split(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location)]" + }, + "lock": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" + }, + "privateDnsZoneGroup": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroup')]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'roleAssignments')]" + }, + "tags": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + }, + "customDnsConfigs": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customDnsConfigs')]" + }, + "ipConfigurations": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'ipConfigurations')]" + }, + "applicationSecurityGroupResourceIds": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'applicationSecurityGroupResourceIds')]" + }, + "customNetworkInterfaceName": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customNetworkInterfaceName')]" + } + }, + "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.34.44.8038", + "templateHash": "12389807800450456797" + }, + "name": "Private Endpoints", + "description": "This module deploys a Private Endpoint." + }, + "definitions": { + "privateDnsZoneGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, + "metadata": { + "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "ipConfigurationType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource that is unique within a resource group." + } + }, + "properties": { + "type": "object", + "properties": { + "groupId": { + "type": "string", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } + } + }, + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "privateLinkServiceConnectionType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the private link service connection." + } + }, + "properties": { + "type": "object", + "properties": { + "groupIds": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." + } + }, + "privateLinkServiceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of private link service." + } + }, + "requestMessage": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." + } + } + }, + "metadata": { + "description": "Required. Properties of private link service connection." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "customDnsConfigType": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. FQDN that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "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." + } + } + }, + "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.5.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": { + "__bicep_imported_from!": { + "sourceTemplate": "private-dns-zone-group/main.bicep" + } + } + }, + "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.5.1" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the private endpoint resource to create." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + } + }, + "applicationSecurityGroupResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Application security groups in which the private endpoint IP configuration is included." + } + }, + "customNetworkInterfaceName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The custom name of the network interface attached to the private endpoint." + } + }, + "ipConfigurations": { + "type": "array", + "items": { + "$ref": "#/definitions/ipConfigurationType" + }, + "nullable": true, + "metadata": { + "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." + } + }, + "privateDnsZoneGroup": { + "$ref": "#/definitions/privateDnsZoneGroupType", + "nullable": true, + "metadata": { + "description": "Optional. The private DNS zone group to configure for the private endpoint." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "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", + "nullable": true, + "metadata": { + "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/customDnsConfigType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Custom DNS configurations." + } + }, + "manualPrivateLinkServiceConnections": { + "type": "array", + "items": { + "$ref": "#/definitions/privateLinkServiceConnectionType" + }, + "nullable": true, + "metadata": { + "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." + } + }, + "privateLinkServiceConnections": { + "type": "array", + "items": { + "$ref": "#/definitions/privateLinkServiceConnectionType" + }, + "nullable": true, + "metadata": { + "description": "Conditional. A grouping of information about the connection to the remote resource. Required if `manualPrivateLinkServiceConnections` is empty." + } + }, + "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": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.11.0', '.', '-'), 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-05-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": "[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": "2022-09-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.34.44.8038", + "templateHash": "13997305779829540948" + }, + "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 + } + } + }, + "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." + } + } + }, + "variables": { + "copy": [ + { + "name": "privateDnsZoneConfigsVar", + "count": "[length(parameters('privateDnsZoneConfigs'))]", + "input": { + "name": "[coalesce(tryGet(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')], 'name'), last(split(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId, '/')))]", + "properties": { + "privateDnsZoneId": "[parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId]" + } + } + } + ] + }, + "resources": { + "privateEndpoint": { + "existing": true, + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2024-05-01", + "name": "[parameters('privateEndpointName')]" + }, + "privateDnsZoneGroup": { + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2024-05-01", + "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", + "properties": { + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" + } + } + }, + "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-05-01', 'full').location]" + }, + "customDnsConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/customDnsConfigType" + }, + "metadata": { + "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": [ + "cognitiveService" + ] + }, + "secretsExport": { + "condition": "[not(equals(parameters('secretsExportConfiguration'), null()))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-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]]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "keyVaultName": { + "value": "[last(split(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '/'))]" + }, + "secretsToSet": { + "value": "[union(createArray(), if(contains(parameters('secretsExportConfiguration'), 'accessKey1Name'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'accessKey1Name'), 'value', listKeys('cognitiveService', '2025-04-01-preview').key1)), createArray()), if(contains(parameters('secretsExportConfiguration'), 'accessKey2Name'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'accessKey2Name'), 'value', listKeys('cognitiveService', '2025-04-01-preview').key2)), createArray()))]" + } + }, + "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.1.42791", + "templateHash": "1200612323329026557" + } + }, + "definitions": { + "secretSetOutputType": { + "type": "object", + "properties": { + "secretResourceId": { + "type": "string", + "metadata": { + "description": "The resourceId of the exported secret." + } + }, + "secretUri": { + "type": "string", + "metadata": { + "description": "The secret URI of the exported secret." + } + }, + "secretUriWithVersion": { + "type": "string", + "metadata": { + "description": "The secret URI with version of the exported secret." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for the output of the secret set via the secrets export feature.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "secretToSetType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the secret to set." + } + }, + "value": { + "type": "securestring", + "metadata": { + "description": "Required. The value of the secret to set." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for the secret to set via the secrets export feature.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + } + }, + "parameters": { + "keyVaultName": { + "type": "string", + "metadata": { + "description": "Required. The name of the Key Vault to set the ecrets in." + } + }, + "secretsToSet": { + "type": "array", + "items": { + "$ref": "#/definitions/secretToSetType" + }, + "metadata": { + "description": "Required. The secrets to set in the Key Vault." + } + } + }, + "resources": { + "keyVault": { + "existing": true, + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2023-07-01", + "name": "[parameters('keyVaultName')]" + }, + "secrets": { + "copy": { + "name": "secrets", + "count": "[length(parameters('secretsToSet'))]" + }, + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2023-07-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('secretsToSet')[copyIndex()].name)]", + "properties": { + "value": "[parameters('secretsToSet')[copyIndex()].value]" + } + } + }, + "outputs": { + "secretsSet": { + "type": "array", + "items": { + "$ref": "#/definitions/secretSetOutputType" + }, + "metadata": { + "description": "The references to the secrets exported to the provided Key Vault." + }, + "copy": { + "count": "[length(range(0, length(coalesce(parameters('secretsToSet'), createArray()))))]", + "input": { + "secretResourceId": "[resourceId('Microsoft.KeyVault/vaults/secrets', parameters('keyVaultName'), parameters('secretsToSet')[range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()]].name)]", + "secretUri": "[reference(format('secrets[{0}]', range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()])).secretUri]", + "secretUriWithVersion": "[reference(format('secrets[{0}]', range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()])).secretUriWithVersion]" + } + } + } + } + } + }, + "dependsOn": [ + "cognitiveService" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the cognitive services account." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the cognitive services account." + }, + "value": "[resourceId('Microsoft.CognitiveServices/accounts', parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the cognitive services account was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "endpoint": { + "type": "string", + "metadata": { + "description": "The service endpoint of the cognitive services account." + }, + "value": "[reference('cognitiveService').endpoint]" + }, + "endpoints": { + "$ref": "#/definitions/endpointType", + "metadata": { + "description": "All endpoints available for the cognitive services account, types depends on the cognitive service kind." + }, + "value": "[reference('cognitiveService').endpoints]" + }, + "systemAssignedMIPrincipalId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "The principal ID of the system assigned identity." + }, + "value": "[tryGet(tryGet(reference('cognitiveService', '2025-04-01-preview', 'full'), 'identity'), 'principalId')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('cognitiveService', '2025-04-01-preview', 'full').location]" + }, + "exportedSecrets": { + "$ref": "#/definitions/secretsOutputType", + "metadata": { + "description": "A hashtable of references to the secrets exported to the provided Key Vault. The key of each reference is each secret's name." + }, + "value": "[if(not(equals(parameters('secretsExportConfiguration'), null())), toObject(reference('secretsExport').outputs.secretsSet.value, lambda('secret', last(split(lambdaVariables('secret').secretResourceId, '/'))), lambda('secret', lambdaVariables('secret'))), createObject())]" + }, + "privateEndpoints": { + "type": "array", + "items": { + "$ref": "#/definitions/privateEndpointOutputType" + }, + "metadata": { + "description": "The private endpoints of the congitive services account." + }, + "copy": { + "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]", + "input": { + "name": "[reference(format('cognitiveService_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", + "resourceId": "[reference(format('cognitiveService_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", + "groupId": "[tryGet(tryGet(reference(format('cognitiveService_privateEndpoints[{0}]', copyIndex())).outputs, 'groupId'), 'value')]", + "customDnsConfigs": "[reference(format('cognitiveService_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfigs.value]", + "networkInterfaceResourceIds": "[reference(format('cognitiveService_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceResourceIds.value]" + } + } + } + } + } + } + } + }, + "outputs": { + "resourceId": { + "type": "string", + "value": "[reference('cognitiveService').outputs.resourceId.value]" + }, + "name": { + "type": "string", + "value": "[reference('cognitiveService').outputs.name.value]" + }, + "systemAssignedMIPrincipalId": { + "type": "string", + "nullable": true, + "value": "[tryGet(tryGet(reference('cognitiveService').outputs, 'systemAssignedMIPrincipalId'), 'value')]" + }, + "endpoint": { + "type": "string", + "value": "[reference('cognitiveService').outputs.endpoint.value]" + }, + "foundryConnection": { + "type": "object", + "value": { + "name": "[reference('cognitiveService').outputs.name.value]", + "value": null, + "category": "[parameters('category')]", + "target": "[reference('cognitiveService').outputs.endpoint.value]", + "kind": "[parameters('kind')]", + "connectionProperties": { + "authType": "AAD" + }, + "isSharedToAll": true, + "metadata": { + "ApiType": "Azure", + "Kind": "[parameters('kind')]", + "ResourceId": "[reference('cognitiveService').outputs.resourceId.value]" + } + } + } + } + } + }, + "dependsOn": [ + "cognitiveServicesPrivateDnsZone", + "openAiPrivateDnsZone" + ] + }, + "contentSafety": { + "condition": "[parameters('contentSafetyEnabled')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('{0}-content-safety-deployment', parameters('name')), 64)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[format('safety{0}{1}', parameters('name'), parameters('resourceToken'))]" + }, + "location": { + "value": "[parameters('location')]" + }, + "kind": { + "value": "ContentSafety" + }, + "networkIsolation": { + "value": "[parameters('networkIsolation')]" + }, + "networkAcls": { + "value": "[parameters('networkAcls')]" + }, + "virtualNetworkSubnetResourceId": "[if(parameters('networkIsolation'), createObject('value', parameters('virtualNetworkSubnetResourceId')), createObject('value', ''))]", + "privateDnsZonesResourceIds": "[if(parameters('networkIsolation'), createObject('value', createArray(reference('cognitiveServicesPrivateDnsZone').outputs.resourceId.value)), createObject('value', createArray()))]", + "logAnalyticsWorkspaceResourceId": { + "value": "[parameters('logAnalyticsWorkspaceResourceId')]" + }, + "roleAssignments": { + "value": "[variables('allRoleAssignments')]" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "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.1.42791", + "templateHash": "2348080591288311162" + } + }, + "definitions": { + "deploymentsType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of cognitive service account deployment." + } + }, + "model": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of Cognitive Services account deployment model." + } + }, + "format": { + "type": "string", + "metadata": { + "description": "Required. The format of Cognitive Services account deployment model." + } + }, + "version": { + "type": "string", + "metadata": { + "description": "Required. The version of Cognitive Services account deployment model." + } + } + }, + "metadata": { + "description": "Required. Properties of Cognitive Services account deployment model." + } + }, + "sku": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource model definition representing SKU." + } + }, + "capacity": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The capacity of the resource model definition representing SKU." + } + }, + "tier": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The tier of the resource model definition representing SKU." + } + }, + "size": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The size of the resource model definition representing SKU." + } + }, + "family": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The family of the resource model definition representing SKU." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The resource model definition representing SKU." + } + }, + "raiPolicyName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of RAI policy." + } + }, + "versionUpgradeOption": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The version upgrade option." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "../customTypes.bicep" + } + } + }, + "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.5.1" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Name of the Cognitive Services resource. Must be unique in the resource group." + } + }, + "location": { + "type": "string", + "metadata": { + "description": "The location of the Cognitive Services resource." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "AIServices", + "AnomalyDetector", + "CognitiveServices", + "ComputerVision", + "ContentModerator", + "ContentSafety", + "ConversationalLanguageUnderstanding", + "CustomVision.Prediction", + "CustomVision.Training", + "Face", + "FormRecognizer", + "HealthInsights", + "ImmersiveReader", + "Internal.AllInOne", + "LUIS", + "LUIS.Authoring", + "LanguageAuthoring", + "MetricsAdvisor", + "OpenAI", + "Personalizer", + "QnAMaker.v2", + "SpeechServices", + "TextAnalytics", + "TextTranslation" + ], + "metadata": { + "description": "Required. Kind of the Cognitive Services account. Use 'Get-AzCognitiveServicesAccountSku' to determine a valid combinations of 'kind' and 'SKU' for your Azure region." + } + }, + "sku": { + "type": "string", + "defaultValue": "S0", + "allowedValues": [ + "S", + "S0", + "S1", + "S2", + "S3", + "S4", + "S5", + "S6", + "S7", + "S8" + ], + "metadata": { + "description": "Required. The SKU of the Cognitive Services account. Use 'Get-AzCognitiveServicesAccountSku' to determine a valid combinations of 'kind' and 'SKU' for your Azure region." + } + }, + "category": { + "type": "string", + "defaultValue": "CognitiveService", + "metadata": { + "description": "Category of the Cognitive Services account." + } + }, + "networkIsolation": { + "type": "bool", + "metadata": { + "description": "Specifies whether to enable network isolation. If true, the resource will be deployed in a private endpoint and public network access will be disabled." + } + }, + "privateDnsZonesResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "defaultValue": [], + "metadata": { + "description": "Existing resource ID of the private DNS zone for the private endpoint." + } + }, + "virtualNetworkSubnetResourceId": { + "type": "string", + "metadata": { + "description": "Resource ID of the subnet for the private endpoint." + } + }, + "logAnalyticsWorkspaceResourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the Log Analytics workspace to use for diagnostic settings." + } + }, + "aiModelDeployments": { + "type": "array", + "items": { + "$ref": "#/definitions/deploymentsType" + }, + "defaultValue": [], + "metadata": { + "description": "Optional. Specifies the OpenAI deployments to create." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Tags to be applied to the resources." + } + }, + "networkAcls": { + "type": "object", + "metadata": { + "description": "Optional. A collection of rules governing the accessibility from specific network locations." + } + } + }, + "variables": { + "copy": [ + { + "name": "privateDnsZones", + "count": "[length(parameters('privateDnsZonesResourceIds'))]", + "input": { + "privateDnsZoneResourceId": "[parameters('privateDnsZonesResourceIds')[copyIndex('privateDnsZones')]]" + } + } + ], + "nameFormatted": "[take(toLower(parameters('name')), 24)]" + }, + "resources": { + "cognitiveService": { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('cog-{0}-{1}-deployment', parameters('kind'), parameters('name')), 64)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[variables('nameFormatted')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "sku": { + "value": "[parameters('sku')]" + }, + "kind": { + "value": "[parameters('kind')]" + }, + "allowProjectManagement": { + "value": true + }, + "managedIdentities": { + "value": { + "systemAssigned": true + } + }, + "deployments": { + "value": "[parameters('aiModelDeployments')]" + }, + "customSubDomainName": { + "value": "[parameters('name')]" + }, + "disableLocalAuth": { + "value": "[parameters('networkIsolation')]" + }, + "publicNetworkAccess": "[if(parameters('networkIsolation'), createObject('value', 'Disabled'), createObject('value', 'Enabled'))]", + "diagnosticSettings": { + "value": [ + { + "workspaceResourceId": "[parameters('logAnalyticsWorkspaceResourceId')]" + } + ] + }, + "roleAssignments": { + "value": "[parameters('roleAssignments')]" + }, + "networkAcls": { + "value": "[parameters('networkAcls')]" + }, + "privateEndpoints": "[if(parameters('networkIsolation'), createObject('value', createArray(createObject('privateDnsZoneGroup', createObject('privateDnsZoneGroupConfigs', variables('privateDnsZones')), 'subnetResourceId', parameters('virtualNetworkSubnetResourceId')))), createObject('value', createArray()))]" + }, + "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.1.42791", + "templateHash": "16135659971302525380" + }, + "name": "Cognitive Services", + "description": "This module deploys a Cognitive Service." + }, + "definitions": { + "privateEndpointOutputType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the private endpoint." + } + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the private endpoint." + } + }, + "groupId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "The group Id for the private endpoint Group." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "FQDN that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "A list of private IP addresses of the private endpoint." + } + } + } + }, + "metadata": { + "description": "The custom DNS configurations of the private endpoint." + } + }, + "networkInterfaceResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "The IDs of the network interfaces associated with the private endpoint." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for the private endpoint output." + } + }, + "deploymentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of cognitive service account deployment." + } + }, + "model": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of Cognitive Services account deployment model." + } + }, + "format": { + "type": "string", + "metadata": { + "description": "Required. The format of Cognitive Services account deployment model." + } + }, + "version": { + "type": "string", + "metadata": { + "description": "Required. The version of Cognitive Services account deployment model." + } + } + }, + "metadata": { + "description": "Required. Properties of Cognitive Services account deployment model." + } + }, + "sku": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource model definition representing SKU." + } + }, + "capacity": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The capacity of the resource model definition representing SKU." + } + }, + "tier": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The tier of the resource model definition representing SKU." + } + }, + "size": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The size of the resource model definition representing SKU." + } + }, + "family": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The family of the resource model definition representing SKU." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The resource model definition representing SKU." + } + }, + "raiPolicyName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of RAI policy." + } + }, + "versionUpgradeOption": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The version upgrade option." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for a cognitive services account deployment." + } + }, + "endpointType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Type of the endpoint." + } + }, + "endpoint": { + "type": "string", + "nullable": true, + "metadata": { + "description": "The endpoint URI." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for a cognitive services account endpoint." + } + }, + "secretsExportConfigurationType": { + "type": "object", + "properties": { + "keyVaultResourceId": { + "type": "string", + "metadata": { + "description": "Required. The key vault name where to store the keys and connection strings generated by the modules." + } + }, + "accessKey1Name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name for the accessKey1 secret to create." + } + }, + "accessKey2Name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name for the accessKey2 secret to create." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type of the secrets exported to the provided Key Vault." + } + }, + "_1.privateEndpointCustomDnsConfigType": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. FQDN that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "_1.privateEndpointIpConfigurationType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource that is unique within a resource group." + } + }, + "properties": { + "type": "object", + "properties": { + "groupId": { + "type": "string", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } + } + }, + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "_1.privateEndpointPrivateDnsZoneGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "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": "Required. The private DNS Zone Groups to associate the Private Endpoint. A DNS Zone Group can support up to 5 DNS zones." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "_1.secretSetOutputType": { + "type": "object", + "properties": { + "secretResourceId": { + "type": "string", + "metadata": { + "description": "The resourceId of the exported secret." + } + }, + "secretUri": { + "type": "string", + "metadata": { + "description": "The secret URI of the exported secret." + } + }, + "secretUriWithVersion": { + "type": "string", + "metadata": { + "description": "The secret URI with version of the exported secret." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for the output of the secret set via the secrets export feature.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "customerManagedKeyType": { + "type": "object", + "properties": { + "keyVaultResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of a key vault to reference a customer managed key for encryption from." + } + }, + "keyName": { + "type": "string", + "metadata": { + "description": "Required. The name of the customer managed key to use for encryption." + } + }, + "keyVersion": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The version of the customer managed key to reference for encryption. If not provided, the deployment will use the latest version available at deployment time." + } + }, + "userAssignedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a customer-managed key. To be used if the resource type does not support auto-rotation of the customer-managed key.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "diagnosticSettingFullType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "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 logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "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 both logs & metrics are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "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." + } + } + }, + "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.5.1" + } + } + }, + "managedIdentityAllType": { + "type": "object", + "properties": { + "systemAssigned": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enables system assigned managed identity on the resource." + } + }, + "userAssignedResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The resource ID(s) to assign to the resource. Required if a user assigned identity is used for encryption." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a managed identity configuration. To be used if both a system-assigned & user-assigned identities are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "privateEndpointSingleServiceType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private Endpoint." + } + }, + "location": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The location to deploy the Private Endpoint to." + } + }, + "privateLinkServiceConnectionName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private link connection to create." + } + }, + "service": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The subresource to deploy the Private Endpoint for. For example \"vault\" for a Key Vault Private Endpoint." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + } + }, + "resourceGroupResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the Resource Group the Private Endpoint will be created in. If not specified, the Resource Group of the provided Virtual Network Subnet is used." + } + }, + "privateDnsZoneGroup": { + "$ref": "#/definitions/_1.privateEndpointPrivateDnsZoneGroupType", + "nullable": true, + "metadata": { + "description": "Optional. The private DNS Zone Group to configure for the Private Endpoint." + } + }, + "isManualConnection": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. If Manual Private Link Connection is required." + } + }, + "manualConnectionRequestMessage": { + "type": "string", + "nullable": true, + "maxLength": 140, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with the manual connection request." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/_1.privateEndpointCustomDnsConfigType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Custom DNS configurations." + } + }, + "ipConfigurations": { + "type": "array", + "items": { + "$ref": "#/definitions/_1.privateEndpointIpConfigurationType" + }, + "nullable": true, + "metadata": { + "description": "Optional. A list of IP configurations of the Private Endpoint. This will be used to map to the first-party Service endpoints." + } + }, + "applicationSecurityGroupResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Application security groups in which the Private Endpoint IP configuration is included." + } + }, + "customNetworkInterfaceName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The custom name of the network interface attached to the Private Endpoint." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags to be applied on all resources/Resource Groups in this deployment." + } + }, + "enableTelemetry": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a private endpoint. To be used if the private endpoint's default service / groupId can be assumed (i.e., for services that only have one Private Endpoint type like 'vault' for key vault).", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "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.5.1" + } + } + }, + "secretsOutputType": { + "type": "object", + "properties": {}, + "additionalProperties": { + "$ref": "#/definitions/_1.secretSetOutputType", + "metadata": { + "description": "An exported secret's references." + } + }, + "metadata": { + "description": "A map of the exported secrets", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of Cognitive Services account." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "AIServices", + "AnomalyDetector", + "CognitiveServices", + "ComputerVision", + "ContentModerator", + "ContentSafety", + "ConversationalLanguageUnderstanding", + "CustomVision.Prediction", + "CustomVision.Training", + "Face", + "FormRecognizer", + "HealthInsights", + "ImmersiveReader", + "Internal.AllInOne", + "LUIS", + "LUIS.Authoring", + "LanguageAuthoring", + "MetricsAdvisor", + "OpenAI", + "Personalizer", + "QnAMaker.v2", + "SpeechServices", + "TextAnalytics", + "TextTranslation" + ], + "metadata": { + "description": "Required. Kind of the Cognitive Services account. Use 'Get-AzCognitiveServicesAccountSku' to determine a valid combinations of 'kind' and 'SKU' for your Azure region." + } + }, + "sku": { + "type": "string", + "defaultValue": "S0", + "allowedValues": [ + "C2", + "C3", + "C4", + "F0", + "F1", + "S", + "S0", + "S1", + "S10", + "S2", + "S3", + "S4", + "S5", + "S6", + "S7", + "S8", + "S9" + ], + "metadata": { + "description": "Optional. SKU of the Cognitive Services account. Use 'Get-AzCognitiveServicesAccountSku' to determine a valid combinations of 'kind' and 'SKU' for your Azure region." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all Resources." + } + }, + "diagnosticSettings": { + "type": "array", + "items": { + "$ref": "#/definitions/diagnosticSettingFullType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "publicNetworkAccess": { + "type": "string", + "nullable": true, + "allowedValues": [ + "Enabled", + "Disabled" + ], + "metadata": { + "description": "Optional. Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set and networkAcls are not set." + } + }, + "customSubDomainName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. Subdomain name used for token-based authentication. Required if 'networkAcls' or 'privateEndpoints' are set." + } + }, + "networkAcls": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. A collection of rules governing the accessibility from specific network locations." + } + }, + "privateEndpoints": { + "type": "array", + "items": { + "$ref": "#/definitions/privateEndpointSingleServiceType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible." + } + }, + "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", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "allowedFqdnList": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. List of allowed FQDN." + } + }, + "apiProperties": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The API properties for special APIs." + } + }, + "disableLocalAuth": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Allow only Azure AD authentication. Should be enabled for security reasons." + } + }, + "customerManagedKey": { + "$ref": "#/definitions/customerManagedKeyType", + "nullable": true, + "metadata": { + "description": "Optional. The customer managed key definition." + } + }, + "dynamicThrottlingEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. The flag to enable dynamic throttling." + } + }, + "migrationToken": { + "type": "securestring", + "nullable": true, + "metadata": { + "description": "Optional. Resource migration token." + } + }, + "restore": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Restore a soft-deleted cognitive service at deployment time. Will fail if no such soft-deleted resource exists." + } + }, + "restrictOutboundNetworkAccess": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Restrict outbound network access." + } + }, + "userOwnedStorage": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The storage accounts for this resource." + } + }, + "managedIdentities": { + "$ref": "#/definitions/managedIdentityAllType", + "nullable": true, + "metadata": { + "description": "Optional. The managed identity definition for this resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "deployments": { + "type": "array", + "items": { + "$ref": "#/definitions/deploymentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of deployments about cognitive service accounts to create." + } + }, + "secretsExportConfiguration": { + "$ref": "#/definitions/secretsExportConfigurationType", + "nullable": true, + "metadata": { + "description": "Optional. Key vault reference and secret settings for the module's secrets export." + } + }, + "allowProjectManagement": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable/Disable project management feature for AI Foundry." + } + } + }, + "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)))))]" + } + ], + "enableReferencedModulesTelemetry": false, + "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", + "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned, UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', null())), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", + "builtInRoleNames": { + "Cognitive Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '25fbc0a9-bd7c-42a3-aa1a-3b75d497ee68')]", + "Cognitive Services Custom Vision Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c1ff6cc2-c111-46fe-8896-e0ef812ad9f3')]", + "Cognitive Services Custom Vision Deployment": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5c4089e1-6d96-4d2f-b296-c1bc7137275f')]", + "Cognitive Services Custom Vision Labeler": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '88424f51-ebe7-446f-bc41-7fa16989e96c')]", + "Cognitive Services Custom Vision Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '93586559-c37d-4a6b-ba08-b9f0940c2d73')]", + "Cognitive Services Custom Vision Trainer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0a5ae4ab-0d65-4eeb-be61-29fc9b54394b')]", + "Cognitive Services Data Reader (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b59867f0-fa02-499b-be73-45a86b5b3e1c')]", + "Cognitive Services Face Recognizer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9894cab4-e18a-44aa-828b-cb588cd6f2d7')]", + "Cognitive Services Immersive Reader User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b2de6794-95db-4659-8781-7e080d3f2b9d')]", + "Cognitive Services Language Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f07febfe-79bc-46b1-8b37-790e26e6e498')]", + "Cognitive Services Language Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7628b7b8-a8b2-4cdc-b46f-e9b35248918e')]", + "Cognitive Services Language Writer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f2310ca1-dc64-4889-bb49-c8e0fa3d47a8')]", + "Cognitive Services LUIS Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f72c8140-2111-481c-87ff-72b910f6e3f8')]", + "Cognitive Services LUIS Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18e81cdc-4e98-4e29-a639-e7d10c5a6226')]", + "Cognitive Services LUIS Writer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6322a993-d5c9-4bed-b113-e49bbea25b27')]", + "Cognitive Services Metrics Advisor Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'cb43c632-a144-4ec5-977c-e80c4affc34a')]", + "Cognitive Services Metrics Advisor User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3b20f47b-3825-43cb-8114-4bd2201156a8')]", + "Cognitive Services OpenAI Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a001fd3d-188f-4b5d-821b-7da978bf7442')]", + "Cognitive Services OpenAI User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e0bd9bd-7b93-4f28-af87-19fc36ad61bd')]", + "Cognitive Services QnA Maker Editor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f4cc2bf9-21be-47a1-bdf1-5c5804381025')]", + "Cognitive Services QnA Maker Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '466ccd10-b268-4a11-b098-b4849f024126')]", + "Cognitive Services Speech Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0e75ca1e-0464-4b4d-8b93-68208a576181')]", + "Cognitive Services Speech User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f2dc8367-1007-4938-bd23-fe263f013447')]", + "Cognitive Services User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a97b65f3-24c7-4388-baec-2e87135dc908')]", + "Azure AI Developer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '64702f94-c441-49e6-a78b-ef80e0188fee')]", + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "cMKKeyVault::cMKKey": { + "condition": "[and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), not(empty(tryGet(parameters('customerManagedKey'), 'keyName')))))]", + "existing": true, + "type": "Microsoft.KeyVault/vaults/keys", + "apiVersion": "2023-07-01", + "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[2]]", + "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[4]]", + "name": "[format('{0}/{1}', last(split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')), tryGet(parameters('customerManagedKey'), 'keyName'))]" + }, + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.cognitiveservices-account.{0}.{1}', replace('0.11.0', '.', '-'), 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" + } + } + } + } + }, + "cMKKeyVault": { + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId')))]", + "existing": true, + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2023-07-01", + "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[2]]", + "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[4]]", + "name": "[last(split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/'))]" + }, + "cMKUserAssignedIdentity": { + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId')))]", + "existing": true, + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2025-01-31-preview", + "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/')[2]]", + "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/')[4]]", + "name": "[last(split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/'))]" + }, + "cognitiveService": { + "type": "Microsoft.CognitiveServices/accounts", + "apiVersion": "2025-04-01-preview", + "name": "[parameters('name')]", + "kind": "[parameters('kind')]", + "identity": "[variables('identity')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "sku": { + "name": "[parameters('sku')]" + }, + "properties": { + "allowProjectManagement": "[parameters('allowProjectManagement')]", + "customSubDomainName": "[parameters('customSubDomainName')]", + "networkAcls": "[if(not(empty(coalesce(parameters('networkAcls'), createObject()))), createObject('defaultAction', tryGet(parameters('networkAcls'), 'defaultAction'), 'virtualNetworkRules', coalesce(tryGet(parameters('networkAcls'), 'virtualNetworkRules'), createArray()), 'ipRules', coalesce(tryGet(parameters('networkAcls'), 'ipRules'), createArray())), null())]", + "publicNetworkAccess": "[if(not(equals(parameters('publicNetworkAccess'), null())), parameters('publicNetworkAccess'), if(not(empty(parameters('networkAcls'))), 'Enabled', 'Disabled'))]", + "allowedFqdnList": "[parameters('allowedFqdnList')]", + "apiProperties": "[parameters('apiProperties')]", + "disableLocalAuth": "[parameters('disableLocalAuth')]", + "encryption": "[if(not(empty(parameters('customerManagedKey'))), createObject('keySource', 'Microsoft.KeyVault', 'keyVaultProperties', createObject('identityClientId', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), ''))), reference('cMKUserAssignedIdentity').clientId, null()), 'keyVaultUri', reference('cMKKeyVault').vaultUri, 'keyName', parameters('customerManagedKey').keyName, 'keyVersion', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'keyVersion'), ''))), tryGet(parameters('customerManagedKey'), 'keyVersion'), last(split(reference('cMKKeyVault::cMKKey').keyUriWithVersion, '/'))))), null())]", + "migrationToken": "[parameters('migrationToken')]", + "restore": "[parameters('restore')]", + "restrictOutboundNetworkAccess": "[parameters('restrictOutboundNetworkAccess')]", + "userOwnedStorage": "[parameters('userOwnedStorage')]", + "dynamicThrottlingEnabled": "[parameters('dynamicThrottlingEnabled')]" + }, + "dependsOn": [ + "cMKKeyVault", + "cMKKeyVault::cMKKey", + "cMKUserAssignedIdentity" + ] + }, + "cognitiveService_deployments": { + "copy": { + "name": "cognitiveService_deployments", + "count": "[length(coalesce(parameters('deployments'), createArray()))]", + "mode": "serial", + "batchSize": 1 + }, + "type": "Microsoft.CognitiveServices/accounts/deployments", + "apiVersion": "2025-04-01-preview", + "name": "[format('{0}/{1}', parameters('name'), coalesce(tryGet(coalesce(parameters('deployments'), createArray())[copyIndex()], 'name'), format('{0}-deployments', parameters('name'))))]", + "properties": { + "model": "[coalesce(parameters('deployments'), createArray())[copyIndex()].model]", + "raiPolicyName": "[tryGet(coalesce(parameters('deployments'), createArray())[copyIndex()], 'raiPolicyName')]", + "versionUpgradeOption": "[tryGet(coalesce(parameters('deployments'), createArray())[copyIndex()], 'versionUpgradeOption')]" + }, + "sku": "[coalesce(tryGet(coalesce(parameters('deployments'), createArray())[copyIndex()], 'sku'), createObject('name', parameters('sku'), 'capacity', tryGet(parameters('sku'), 'capacity'), 'tier', tryGet(parameters('sku'), 'tier'), 'size', tryGet(parameters('sku'), 'size'), 'family', tryGet(parameters('sku'), 'family')))]", + "dependsOn": [ + "cognitiveService" + ] + }, + "cognitiveService_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.CognitiveServices/accounts/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "cognitiveService" + ] + }, + "cognitiveService_diagnosticSettings": { + "copy": { + "name": "cognitiveService_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.CognitiveServices/accounts/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null + } + }, + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "cognitiveService" + ] + }, + "cognitiveService_roleAssignments": { + "copy": { + "name": "cognitiveService_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.CognitiveServices/accounts/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.CognitiveServices/accounts', 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": [ + "cognitiveService" + ] + }, + "cognitiveService_privateEndpoints": { + "copy": { + "name": "cognitiveService_privateEndpoints", + "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-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]]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'name'), format('pep-{0}-{1}-{2}', last(split(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'account'), copyIndex()))]" + }, + "privateLinkServiceConnections": "[if(not(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true())), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'account'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'account')))))), createObject('value', null()))]", + "manualPrivateLinkServiceConnections": "[if(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true()), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'account'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'account')), 'requestMessage', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualConnectionRequestMessage'), 'Manual approval required.'))))), createObject('value', null()))]", + "subnetResourceId": { + "value": "[coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId]" + }, + "enableTelemetry": { + "value": "[variables('enableReferencedModulesTelemetry')]" + }, + "location": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'location'), reference(split(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location)]" + }, + "lock": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" + }, + "privateDnsZoneGroup": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroup')]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'roleAssignments')]" + }, + "tags": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + }, + "customDnsConfigs": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customDnsConfigs')]" + }, + "ipConfigurations": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'ipConfigurations')]" + }, + "applicationSecurityGroupResourceIds": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'applicationSecurityGroupResourceIds')]" + }, + "customNetworkInterfaceName": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customNetworkInterfaceName')]" + } + }, + "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.34.44.8038", + "templateHash": "12389807800450456797" + }, + "name": "Private Endpoints", + "description": "This module deploys a Private Endpoint." + }, + "definitions": { + "privateDnsZoneGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, + "metadata": { + "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "ipConfigurationType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource that is unique within a resource group." + } + }, + "properties": { + "type": "object", + "properties": { + "groupId": { + "type": "string", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } + } + }, + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "privateLinkServiceConnectionType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the private link service connection." + } + }, + "properties": { + "type": "object", + "properties": { + "groupIds": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." + } + }, + "privateLinkServiceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of private link service." + } + }, + "requestMessage": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." + } + } + }, + "metadata": { + "description": "Required. Properties of private link service connection." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "customDnsConfigType": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. FQDN that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "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." + } + } + }, + "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.5.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": { + "__bicep_imported_from!": { + "sourceTemplate": "private-dns-zone-group/main.bicep" + } + } + }, + "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.5.1" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the private endpoint resource to create." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + } + }, + "applicationSecurityGroupResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Application security groups in which the private endpoint IP configuration is included." + } + }, + "customNetworkInterfaceName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The custom name of the network interface attached to the private endpoint." + } + }, + "ipConfigurations": { + "type": "array", + "items": { + "$ref": "#/definitions/ipConfigurationType" + }, + "nullable": true, + "metadata": { + "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." + } + }, + "privateDnsZoneGroup": { + "$ref": "#/definitions/privateDnsZoneGroupType", + "nullable": true, + "metadata": { + "description": "Optional. The private DNS zone group to configure for the private endpoint." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "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", + "nullable": true, + "metadata": { + "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/customDnsConfigType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Custom DNS configurations." + } + }, + "manualPrivateLinkServiceConnections": { + "type": "array", + "items": { + "$ref": "#/definitions/privateLinkServiceConnectionType" + }, + "nullable": true, + "metadata": { + "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." + } + }, + "privateLinkServiceConnections": { + "type": "array", + "items": { + "$ref": "#/definitions/privateLinkServiceConnectionType" + }, + "nullable": true, + "metadata": { + "description": "Conditional. A grouping of information about the connection to the remote resource. Required if `manualPrivateLinkServiceConnections` is empty." + } + }, + "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": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.11.0', '.', '-'), 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-05-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": "[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": "2022-09-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.34.44.8038", + "templateHash": "13997305779829540948" + }, + "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 + } + } + }, + "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." + } + } + }, + "variables": { + "copy": [ + { + "name": "privateDnsZoneConfigsVar", + "count": "[length(parameters('privateDnsZoneConfigs'))]", + "input": { + "name": "[coalesce(tryGet(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')], 'name'), last(split(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId, '/')))]", + "properties": { + "privateDnsZoneId": "[parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId]" + } + } + } + ] + }, + "resources": { + "privateEndpoint": { + "existing": true, + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2024-05-01", + "name": "[parameters('privateEndpointName')]" + }, + "privateDnsZoneGroup": { + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2024-05-01", + "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", + "properties": { + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" + } + } + }, + "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-05-01', 'full').location]" + }, + "customDnsConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/customDnsConfigType" + }, + "metadata": { + "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": [ + "cognitiveService" + ] + }, + "secretsExport": { + "condition": "[not(equals(parameters('secretsExportConfiguration'), null()))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-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]]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "keyVaultName": { + "value": "[last(split(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '/'))]" + }, + "secretsToSet": { + "value": "[union(createArray(), if(contains(parameters('secretsExportConfiguration'), 'accessKey1Name'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'accessKey1Name'), 'value', listKeys('cognitiveService', '2025-04-01-preview').key1)), createArray()), if(contains(parameters('secretsExportConfiguration'), 'accessKey2Name'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'accessKey2Name'), 'value', listKeys('cognitiveService', '2025-04-01-preview').key2)), createArray()))]" + } + }, + "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.1.42791", + "templateHash": "1200612323329026557" + } + }, + "definitions": { + "secretSetOutputType": { + "type": "object", + "properties": { + "secretResourceId": { + "type": "string", + "metadata": { + "description": "The resourceId of the exported secret." + } + }, + "secretUri": { + "type": "string", + "metadata": { + "description": "The secret URI of the exported secret." + } + }, + "secretUriWithVersion": { + "type": "string", + "metadata": { + "description": "The secret URI with version of the exported secret." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for the output of the secret set via the secrets export feature.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "secretToSetType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the secret to set." + } + }, + "value": { + "type": "securestring", + "metadata": { + "description": "Required. The value of the secret to set." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for the secret to set via the secrets export feature.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + } + }, + "parameters": { + "keyVaultName": { + "type": "string", + "metadata": { + "description": "Required. The name of the Key Vault to set the ecrets in." + } + }, + "secretsToSet": { + "type": "array", + "items": { + "$ref": "#/definitions/secretToSetType" + }, + "metadata": { + "description": "Required. The secrets to set in the Key Vault." + } + } + }, + "resources": { + "keyVault": { + "existing": true, + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2023-07-01", + "name": "[parameters('keyVaultName')]" + }, + "secrets": { + "copy": { + "name": "secrets", + "count": "[length(parameters('secretsToSet'))]" + }, + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2023-07-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('secretsToSet')[copyIndex()].name)]", + "properties": { + "value": "[parameters('secretsToSet')[copyIndex()].value]" + } + } + }, + "outputs": { + "secretsSet": { + "type": "array", + "items": { + "$ref": "#/definitions/secretSetOutputType" + }, + "metadata": { + "description": "The references to the secrets exported to the provided Key Vault." + }, + "copy": { + "count": "[length(range(0, length(coalesce(parameters('secretsToSet'), createArray()))))]", + "input": { + "secretResourceId": "[resourceId('Microsoft.KeyVault/vaults/secrets', parameters('keyVaultName'), parameters('secretsToSet')[range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()]].name)]", + "secretUri": "[reference(format('secrets[{0}]', range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()])).secretUri]", + "secretUriWithVersion": "[reference(format('secrets[{0}]', range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()])).secretUriWithVersion]" + } + } + } + } + } + }, + "dependsOn": [ + "cognitiveService" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the cognitive services account." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the cognitive services account." + }, + "value": "[resourceId('Microsoft.CognitiveServices/accounts', parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the cognitive services account was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "endpoint": { + "type": "string", + "metadata": { + "description": "The service endpoint of the cognitive services account." + }, + "value": "[reference('cognitiveService').endpoint]" + }, + "endpoints": { + "$ref": "#/definitions/endpointType", + "metadata": { + "description": "All endpoints available for the cognitive services account, types depends on the cognitive service kind." + }, + "value": "[reference('cognitiveService').endpoints]" + }, + "systemAssignedMIPrincipalId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "The principal ID of the system assigned identity." + }, + "value": "[tryGet(tryGet(reference('cognitiveService', '2025-04-01-preview', 'full'), 'identity'), 'principalId')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('cognitiveService', '2025-04-01-preview', 'full').location]" + }, + "exportedSecrets": { + "$ref": "#/definitions/secretsOutputType", + "metadata": { + "description": "A hashtable of references to the secrets exported to the provided Key Vault. The key of each reference is each secret's name." + }, + "value": "[if(not(equals(parameters('secretsExportConfiguration'), null())), toObject(reference('secretsExport').outputs.secretsSet.value, lambda('secret', last(split(lambdaVariables('secret').secretResourceId, '/'))), lambda('secret', lambdaVariables('secret'))), createObject())]" + }, + "privateEndpoints": { + "type": "array", + "items": { + "$ref": "#/definitions/privateEndpointOutputType" + }, + "metadata": { + "description": "The private endpoints of the congitive services account." + }, + "copy": { + "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]", + "input": { + "name": "[reference(format('cognitiveService_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", + "resourceId": "[reference(format('cognitiveService_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", + "groupId": "[tryGet(tryGet(reference(format('cognitiveService_privateEndpoints[{0}]', copyIndex())).outputs, 'groupId'), 'value')]", + "customDnsConfigs": "[reference(format('cognitiveService_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfigs.value]", + "networkInterfaceResourceIds": "[reference(format('cognitiveService_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceResourceIds.value]" + } + } + } + } + } + } + } + }, + "outputs": { + "resourceId": { + "type": "string", + "value": "[reference('cognitiveService').outputs.resourceId.value]" + }, + "name": { + "type": "string", + "value": "[reference('cognitiveService').outputs.name.value]" + }, + "systemAssignedMIPrincipalId": { + "type": "string", + "nullable": true, + "value": "[tryGet(tryGet(reference('cognitiveService').outputs, 'systemAssignedMIPrincipalId'), 'value')]" + }, + "endpoint": { + "type": "string", + "value": "[reference('cognitiveService').outputs.endpoint.value]" + }, + "foundryConnection": { + "type": "object", + "value": { + "name": "[reference('cognitiveService').outputs.name.value]", + "value": null, + "category": "[parameters('category')]", + "target": "[reference('cognitiveService').outputs.endpoint.value]", + "kind": "[parameters('kind')]", + "connectionProperties": { + "authType": "AAD" + }, + "isSharedToAll": true, + "metadata": { + "ApiType": "Azure", + "Kind": "[parameters('kind')]", + "ResourceId": "[reference('cognitiveService').outputs.resourceId.value]" + } + } + } + } + } + }, + "dependsOn": [ + "cognitiveServicesPrivateDnsZone" + ] + }, + "vision": { + "condition": "[parameters('visionEnabled')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('{0}-vision-deployment', parameters('name')), 64)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[format('vision{0}{1}', parameters('name'), parameters('resourceToken'))]" + }, + "location": { + "value": "[parameters('location')]" + }, + "kind": { + "value": "ComputerVision" + }, + "sku": { + "value": "S1" + }, + "networkIsolation": { + "value": "[parameters('networkIsolation')]" + }, + "networkAcls": { + "value": "[parameters('networkAcls')]" + }, + "virtualNetworkSubnetResourceId": "[if(parameters('networkIsolation'), createObject('value', parameters('virtualNetworkSubnetResourceId')), createObject('value', ''))]", + "privateDnsZonesResourceIds": "[if(parameters('networkIsolation'), createObject('value', createArray(reference('cognitiveServicesPrivateDnsZone').outputs.resourceId.value)), createObject('value', createArray()))]", + "logAnalyticsWorkspaceResourceId": { + "value": "[parameters('logAnalyticsWorkspaceResourceId')]" + }, + "roleAssignments": { + "value": "[variables('allRoleAssignments')]" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "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.1.42791", + "templateHash": "2348080591288311162" + } + }, + "definitions": { + "deploymentsType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of cognitive service account deployment." + } + }, + "model": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of Cognitive Services account deployment model." + } + }, + "format": { + "type": "string", + "metadata": { + "description": "Required. The format of Cognitive Services account deployment model." + } + }, + "version": { + "type": "string", + "metadata": { + "description": "Required. The version of Cognitive Services account deployment model." + } + } + }, + "metadata": { + "description": "Required. Properties of Cognitive Services account deployment model." + } + }, + "sku": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource model definition representing SKU." + } + }, + "capacity": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The capacity of the resource model definition representing SKU." + } + }, + "tier": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The tier of the resource model definition representing SKU." + } + }, + "size": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The size of the resource model definition representing SKU." + } + }, + "family": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The family of the resource model definition representing SKU." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The resource model definition representing SKU." + } + }, + "raiPolicyName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of RAI policy." + } + }, + "versionUpgradeOption": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The version upgrade option." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "../customTypes.bicep" + } + } + }, + "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.5.1" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Name of the Cognitive Services resource. Must be unique in the resource group." + } + }, + "location": { + "type": "string", + "metadata": { + "description": "The location of the Cognitive Services resource." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "AIServices", + "AnomalyDetector", + "CognitiveServices", + "ComputerVision", + "ContentModerator", + "ContentSafety", + "ConversationalLanguageUnderstanding", + "CustomVision.Prediction", + "CustomVision.Training", + "Face", + "FormRecognizer", + "HealthInsights", + "ImmersiveReader", + "Internal.AllInOne", + "LUIS", + "LUIS.Authoring", + "LanguageAuthoring", + "MetricsAdvisor", + "OpenAI", + "Personalizer", + "QnAMaker.v2", + "SpeechServices", + "TextAnalytics", + "TextTranslation" + ], + "metadata": { + "description": "Required. Kind of the Cognitive Services account. Use 'Get-AzCognitiveServicesAccountSku' to determine a valid combinations of 'kind' and 'SKU' for your Azure region." + } + }, + "sku": { + "type": "string", + "defaultValue": "S0", + "allowedValues": [ + "S", + "S0", + "S1", + "S2", + "S3", + "S4", + "S5", + "S6", + "S7", + "S8" + ], + "metadata": { + "description": "Required. The SKU of the Cognitive Services account. Use 'Get-AzCognitiveServicesAccountSku' to determine a valid combinations of 'kind' and 'SKU' for your Azure region." + } + }, + "category": { + "type": "string", + "defaultValue": "CognitiveService", + "metadata": { + "description": "Category of the Cognitive Services account." + } + }, + "networkIsolation": { + "type": "bool", + "metadata": { + "description": "Specifies whether to enable network isolation. If true, the resource will be deployed in a private endpoint and public network access will be disabled." + } + }, + "privateDnsZonesResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "defaultValue": [], + "metadata": { + "description": "Existing resource ID of the private DNS zone for the private endpoint." + } + }, + "virtualNetworkSubnetResourceId": { + "type": "string", + "metadata": { + "description": "Resource ID of the subnet for the private endpoint." + } + }, + "logAnalyticsWorkspaceResourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the Log Analytics workspace to use for diagnostic settings." + } + }, + "aiModelDeployments": { + "type": "array", + "items": { + "$ref": "#/definitions/deploymentsType" + }, + "defaultValue": [], + "metadata": { + "description": "Optional. Specifies the OpenAI deployments to create." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Tags to be applied to the resources." + } + }, + "networkAcls": { + "type": "object", + "metadata": { + "description": "Optional. A collection of rules governing the accessibility from specific network locations." + } + } + }, + "variables": { + "copy": [ + { + "name": "privateDnsZones", + "count": "[length(parameters('privateDnsZonesResourceIds'))]", + "input": { + "privateDnsZoneResourceId": "[parameters('privateDnsZonesResourceIds')[copyIndex('privateDnsZones')]]" + } + } + ], + "nameFormatted": "[take(toLower(parameters('name')), 24)]" + }, + "resources": { + "cognitiveService": { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('cog-{0}-{1}-deployment', parameters('kind'), parameters('name')), 64)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[variables('nameFormatted')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "sku": { + "value": "[parameters('sku')]" + }, + "kind": { + "value": "[parameters('kind')]" + }, + "allowProjectManagement": { + "value": true + }, + "managedIdentities": { + "value": { + "systemAssigned": true + } + }, + "deployments": { + "value": "[parameters('aiModelDeployments')]" + }, + "customSubDomainName": { + "value": "[parameters('name')]" + }, + "disableLocalAuth": { + "value": "[parameters('networkIsolation')]" + }, + "publicNetworkAccess": "[if(parameters('networkIsolation'), createObject('value', 'Disabled'), createObject('value', 'Enabled'))]", + "diagnosticSettings": { + "value": [ + { + "workspaceResourceId": "[parameters('logAnalyticsWorkspaceResourceId')]" + } + ] + }, + "roleAssignments": { + "value": "[parameters('roleAssignments')]" + }, + "networkAcls": { + "value": "[parameters('networkAcls')]" + }, + "privateEndpoints": "[if(parameters('networkIsolation'), createObject('value', createArray(createObject('privateDnsZoneGroup', createObject('privateDnsZoneGroupConfigs', variables('privateDnsZones')), 'subnetResourceId', parameters('virtualNetworkSubnetResourceId')))), createObject('value', createArray()))]" + }, + "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.1.42791", + "templateHash": "16135659971302525380" + }, + "name": "Cognitive Services", + "description": "This module deploys a Cognitive Service." + }, + "definitions": { + "privateEndpointOutputType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the private endpoint." + } + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the private endpoint." + } + }, + "groupId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "The group Id for the private endpoint Group." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "FQDN that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "A list of private IP addresses of the private endpoint." + } + } + } + }, + "metadata": { + "description": "The custom DNS configurations of the private endpoint." + } + }, + "networkInterfaceResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "The IDs of the network interfaces associated with the private endpoint." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for the private endpoint output." + } + }, + "deploymentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of cognitive service account deployment." + } + }, + "model": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of Cognitive Services account deployment model." + } + }, + "format": { + "type": "string", + "metadata": { + "description": "Required. The format of Cognitive Services account deployment model." + } + }, + "version": { + "type": "string", + "metadata": { + "description": "Required. The version of Cognitive Services account deployment model." + } + } + }, + "metadata": { + "description": "Required. Properties of Cognitive Services account deployment model." + } + }, + "sku": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource model definition representing SKU." + } + }, + "capacity": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The capacity of the resource model definition representing SKU." + } + }, + "tier": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The tier of the resource model definition representing SKU." + } + }, + "size": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The size of the resource model definition representing SKU." + } + }, + "family": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The family of the resource model definition representing SKU." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The resource model definition representing SKU." + } + }, + "raiPolicyName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of RAI policy." + } + }, + "versionUpgradeOption": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The version upgrade option." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for a cognitive services account deployment." + } + }, + "endpointType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Type of the endpoint." + } + }, + "endpoint": { + "type": "string", + "nullable": true, + "metadata": { + "description": "The endpoint URI." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for a cognitive services account endpoint." + } + }, + "secretsExportConfigurationType": { + "type": "object", + "properties": { + "keyVaultResourceId": { + "type": "string", + "metadata": { + "description": "Required. The key vault name where to store the keys and connection strings generated by the modules." + } + }, + "accessKey1Name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name for the accessKey1 secret to create." + } + }, + "accessKey2Name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name for the accessKey2 secret to create." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type of the secrets exported to the provided Key Vault." + } + }, + "_1.privateEndpointCustomDnsConfigType": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. FQDN that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "_1.privateEndpointIpConfigurationType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource that is unique within a resource group." + } + }, + "properties": { + "type": "object", + "properties": { + "groupId": { + "type": "string", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } + } + }, + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "_1.privateEndpointPrivateDnsZoneGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "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": "Required. The private DNS Zone Groups to associate the Private Endpoint. A DNS Zone Group can support up to 5 DNS zones." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "_1.secretSetOutputType": { + "type": "object", + "properties": { + "secretResourceId": { + "type": "string", + "metadata": { + "description": "The resourceId of the exported secret." + } + }, + "secretUri": { + "type": "string", + "metadata": { + "description": "The secret URI of the exported secret." + } + }, + "secretUriWithVersion": { + "type": "string", + "metadata": { + "description": "The secret URI with version of the exported secret." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for the output of the secret set via the secrets export feature.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "customerManagedKeyType": { + "type": "object", + "properties": { + "keyVaultResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of a key vault to reference a customer managed key for encryption from." + } + }, + "keyName": { + "type": "string", + "metadata": { + "description": "Required. The name of the customer managed key to use for encryption." + } + }, + "keyVersion": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The version of the customer managed key to reference for encryption. If not provided, the deployment will use the latest version available at deployment time." + } + }, + "userAssignedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a customer-managed key. To be used if the resource type does not support auto-rotation of the customer-managed key.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "diagnosticSettingFullType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "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 logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "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 both logs & metrics are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "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." + } + } + }, + "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.5.1" + } + } + }, + "managedIdentityAllType": { + "type": "object", + "properties": { + "systemAssigned": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enables system assigned managed identity on the resource." + } + }, + "userAssignedResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The resource ID(s) to assign to the resource. Required if a user assigned identity is used for encryption." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a managed identity configuration. To be used if both a system-assigned & user-assigned identities are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "privateEndpointSingleServiceType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private Endpoint." + } + }, + "location": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The location to deploy the Private Endpoint to." + } + }, + "privateLinkServiceConnectionName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private link connection to create." + } + }, + "service": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The subresource to deploy the Private Endpoint for. For example \"vault\" for a Key Vault Private Endpoint." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + } + }, + "resourceGroupResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the Resource Group the Private Endpoint will be created in. If not specified, the Resource Group of the provided Virtual Network Subnet is used." + } + }, + "privateDnsZoneGroup": { + "$ref": "#/definitions/_1.privateEndpointPrivateDnsZoneGroupType", + "nullable": true, + "metadata": { + "description": "Optional. The private DNS Zone Group to configure for the Private Endpoint." + } + }, + "isManualConnection": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. If Manual Private Link Connection is required." + } + }, + "manualConnectionRequestMessage": { + "type": "string", + "nullable": true, + "maxLength": 140, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with the manual connection request." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/_1.privateEndpointCustomDnsConfigType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Custom DNS configurations." + } + }, + "ipConfigurations": { + "type": "array", + "items": { + "$ref": "#/definitions/_1.privateEndpointIpConfigurationType" + }, + "nullable": true, + "metadata": { + "description": "Optional. A list of IP configurations of the Private Endpoint. This will be used to map to the first-party Service endpoints." + } + }, + "applicationSecurityGroupResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Application security groups in which the Private Endpoint IP configuration is included." + } + }, + "customNetworkInterfaceName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The custom name of the network interface attached to the Private Endpoint." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags to be applied on all resources/Resource Groups in this deployment." + } + }, + "enableTelemetry": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a private endpoint. To be used if the private endpoint's default service / groupId can be assumed (i.e., for services that only have one Private Endpoint type like 'vault' for key vault).", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "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.5.1" + } + } + }, + "secretsOutputType": { + "type": "object", + "properties": {}, + "additionalProperties": { + "$ref": "#/definitions/_1.secretSetOutputType", + "metadata": { + "description": "An exported secret's references." + } + }, + "metadata": { + "description": "A map of the exported secrets", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of Cognitive Services account." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "AIServices", + "AnomalyDetector", + "CognitiveServices", + "ComputerVision", + "ContentModerator", + "ContentSafety", + "ConversationalLanguageUnderstanding", + "CustomVision.Prediction", + "CustomVision.Training", + "Face", + "FormRecognizer", + "HealthInsights", + "ImmersiveReader", + "Internal.AllInOne", + "LUIS", + "LUIS.Authoring", + "LanguageAuthoring", + "MetricsAdvisor", + "OpenAI", + "Personalizer", + "QnAMaker.v2", + "SpeechServices", + "TextAnalytics", + "TextTranslation" + ], + "metadata": { + "description": "Required. Kind of the Cognitive Services account. Use 'Get-AzCognitiveServicesAccountSku' to determine a valid combinations of 'kind' and 'SKU' for your Azure region." + } + }, + "sku": { + "type": "string", + "defaultValue": "S0", + "allowedValues": [ + "C2", + "C3", + "C4", + "F0", + "F1", + "S", + "S0", + "S1", + "S10", + "S2", + "S3", + "S4", + "S5", + "S6", + "S7", + "S8", + "S9" + ], + "metadata": { + "description": "Optional. SKU of the Cognitive Services account. Use 'Get-AzCognitiveServicesAccountSku' to determine a valid combinations of 'kind' and 'SKU' for your Azure region." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all Resources." + } + }, + "diagnosticSettings": { + "type": "array", + "items": { + "$ref": "#/definitions/diagnosticSettingFullType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "publicNetworkAccess": { + "type": "string", + "nullable": true, + "allowedValues": [ + "Enabled", + "Disabled" + ], + "metadata": { + "description": "Optional. Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set and networkAcls are not set." + } + }, + "customSubDomainName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. Subdomain name used for token-based authentication. Required if 'networkAcls' or 'privateEndpoints' are set." + } + }, + "networkAcls": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. A collection of rules governing the accessibility from specific network locations." + } + }, + "privateEndpoints": { + "type": "array", + "items": { + "$ref": "#/definitions/privateEndpointSingleServiceType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible." + } + }, + "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", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "allowedFqdnList": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. List of allowed FQDN." + } + }, + "apiProperties": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The API properties for special APIs." + } + }, + "disableLocalAuth": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Allow only Azure AD authentication. Should be enabled for security reasons." + } + }, + "customerManagedKey": { + "$ref": "#/definitions/customerManagedKeyType", + "nullable": true, + "metadata": { + "description": "Optional. The customer managed key definition." + } + }, + "dynamicThrottlingEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. The flag to enable dynamic throttling." + } + }, + "migrationToken": { + "type": "securestring", + "nullable": true, + "metadata": { + "description": "Optional. Resource migration token." + } + }, + "restore": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Restore a soft-deleted cognitive service at deployment time. Will fail if no such soft-deleted resource exists." + } + }, + "restrictOutboundNetworkAccess": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Restrict outbound network access." + } + }, + "userOwnedStorage": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The storage accounts for this resource." + } + }, + "managedIdentities": { + "$ref": "#/definitions/managedIdentityAllType", + "nullable": true, + "metadata": { + "description": "Optional. The managed identity definition for this resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "deployments": { + "type": "array", + "items": { + "$ref": "#/definitions/deploymentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of deployments about cognitive service accounts to create." + } + }, + "secretsExportConfiguration": { + "$ref": "#/definitions/secretsExportConfigurationType", + "nullable": true, + "metadata": { + "description": "Optional. Key vault reference and secret settings for the module's secrets export." + } + }, + "allowProjectManagement": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable/Disable project management feature for AI Foundry." + } + } + }, + "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)))))]" + } + ], + "enableReferencedModulesTelemetry": false, + "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", + "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned, UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', null())), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", + "builtInRoleNames": { + "Cognitive Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '25fbc0a9-bd7c-42a3-aa1a-3b75d497ee68')]", + "Cognitive Services Custom Vision Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c1ff6cc2-c111-46fe-8896-e0ef812ad9f3')]", + "Cognitive Services Custom Vision Deployment": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5c4089e1-6d96-4d2f-b296-c1bc7137275f')]", + "Cognitive Services Custom Vision Labeler": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '88424f51-ebe7-446f-bc41-7fa16989e96c')]", + "Cognitive Services Custom Vision Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '93586559-c37d-4a6b-ba08-b9f0940c2d73')]", + "Cognitive Services Custom Vision Trainer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0a5ae4ab-0d65-4eeb-be61-29fc9b54394b')]", + "Cognitive Services Data Reader (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b59867f0-fa02-499b-be73-45a86b5b3e1c')]", + "Cognitive Services Face Recognizer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9894cab4-e18a-44aa-828b-cb588cd6f2d7')]", + "Cognitive Services Immersive Reader User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b2de6794-95db-4659-8781-7e080d3f2b9d')]", + "Cognitive Services Language Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f07febfe-79bc-46b1-8b37-790e26e6e498')]", + "Cognitive Services Language Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7628b7b8-a8b2-4cdc-b46f-e9b35248918e')]", + "Cognitive Services Language Writer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f2310ca1-dc64-4889-bb49-c8e0fa3d47a8')]", + "Cognitive Services LUIS Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f72c8140-2111-481c-87ff-72b910f6e3f8')]", + "Cognitive Services LUIS Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18e81cdc-4e98-4e29-a639-e7d10c5a6226')]", + "Cognitive Services LUIS Writer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6322a993-d5c9-4bed-b113-e49bbea25b27')]", + "Cognitive Services Metrics Advisor Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'cb43c632-a144-4ec5-977c-e80c4affc34a')]", + "Cognitive Services Metrics Advisor User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3b20f47b-3825-43cb-8114-4bd2201156a8')]", + "Cognitive Services OpenAI Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a001fd3d-188f-4b5d-821b-7da978bf7442')]", + "Cognitive Services OpenAI User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e0bd9bd-7b93-4f28-af87-19fc36ad61bd')]", + "Cognitive Services QnA Maker Editor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f4cc2bf9-21be-47a1-bdf1-5c5804381025')]", + "Cognitive Services QnA Maker Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '466ccd10-b268-4a11-b098-b4849f024126')]", + "Cognitive Services Speech Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0e75ca1e-0464-4b4d-8b93-68208a576181')]", + "Cognitive Services Speech User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f2dc8367-1007-4938-bd23-fe263f013447')]", + "Cognitive Services User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a97b65f3-24c7-4388-baec-2e87135dc908')]", + "Azure AI Developer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '64702f94-c441-49e6-a78b-ef80e0188fee')]", + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "cMKKeyVault::cMKKey": { + "condition": "[and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), not(empty(tryGet(parameters('customerManagedKey'), 'keyName')))))]", + "existing": true, + "type": "Microsoft.KeyVault/vaults/keys", + "apiVersion": "2023-07-01", + "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[2]]", + "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[4]]", + "name": "[format('{0}/{1}', last(split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')), tryGet(parameters('customerManagedKey'), 'keyName'))]" + }, + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.cognitiveservices-account.{0}.{1}', replace('0.11.0', '.', '-'), 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" + } + } + } + } + }, + "cMKKeyVault": { + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId')))]", + "existing": true, + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2023-07-01", + "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[2]]", + "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[4]]", + "name": "[last(split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/'))]" + }, + "cMKUserAssignedIdentity": { + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId')))]", + "existing": true, + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2025-01-31-preview", + "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/')[2]]", + "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/')[4]]", + "name": "[last(split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/'))]" + }, + "cognitiveService": { + "type": "Microsoft.CognitiveServices/accounts", + "apiVersion": "2025-04-01-preview", + "name": "[parameters('name')]", + "kind": "[parameters('kind')]", + "identity": "[variables('identity')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "sku": { + "name": "[parameters('sku')]" + }, + "properties": { + "allowProjectManagement": "[parameters('allowProjectManagement')]", + "customSubDomainName": "[parameters('customSubDomainName')]", + "networkAcls": "[if(not(empty(coalesce(parameters('networkAcls'), createObject()))), createObject('defaultAction', tryGet(parameters('networkAcls'), 'defaultAction'), 'virtualNetworkRules', coalesce(tryGet(parameters('networkAcls'), 'virtualNetworkRules'), createArray()), 'ipRules', coalesce(tryGet(parameters('networkAcls'), 'ipRules'), createArray())), null())]", + "publicNetworkAccess": "[if(not(equals(parameters('publicNetworkAccess'), null())), parameters('publicNetworkAccess'), if(not(empty(parameters('networkAcls'))), 'Enabled', 'Disabled'))]", + "allowedFqdnList": "[parameters('allowedFqdnList')]", + "apiProperties": "[parameters('apiProperties')]", + "disableLocalAuth": "[parameters('disableLocalAuth')]", + "encryption": "[if(not(empty(parameters('customerManagedKey'))), createObject('keySource', 'Microsoft.KeyVault', 'keyVaultProperties', createObject('identityClientId', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), ''))), reference('cMKUserAssignedIdentity').clientId, null()), 'keyVaultUri', reference('cMKKeyVault').vaultUri, 'keyName', parameters('customerManagedKey').keyName, 'keyVersion', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'keyVersion'), ''))), tryGet(parameters('customerManagedKey'), 'keyVersion'), last(split(reference('cMKKeyVault::cMKKey').keyUriWithVersion, '/'))))), null())]", + "migrationToken": "[parameters('migrationToken')]", + "restore": "[parameters('restore')]", + "restrictOutboundNetworkAccess": "[parameters('restrictOutboundNetworkAccess')]", + "userOwnedStorage": "[parameters('userOwnedStorage')]", + "dynamicThrottlingEnabled": "[parameters('dynamicThrottlingEnabled')]" + }, + "dependsOn": [ + "cMKKeyVault", + "cMKKeyVault::cMKKey", + "cMKUserAssignedIdentity" + ] + }, + "cognitiveService_deployments": { + "copy": { + "name": "cognitiveService_deployments", + "count": "[length(coalesce(parameters('deployments'), createArray()))]", + "mode": "serial", + "batchSize": 1 + }, + "type": "Microsoft.CognitiveServices/accounts/deployments", + "apiVersion": "2025-04-01-preview", + "name": "[format('{0}/{1}', parameters('name'), coalesce(tryGet(coalesce(parameters('deployments'), createArray())[copyIndex()], 'name'), format('{0}-deployments', parameters('name'))))]", + "properties": { + "model": "[coalesce(parameters('deployments'), createArray())[copyIndex()].model]", + "raiPolicyName": "[tryGet(coalesce(parameters('deployments'), createArray())[copyIndex()], 'raiPolicyName')]", + "versionUpgradeOption": "[tryGet(coalesce(parameters('deployments'), createArray())[copyIndex()], 'versionUpgradeOption')]" + }, + "sku": "[coalesce(tryGet(coalesce(parameters('deployments'), createArray())[copyIndex()], 'sku'), createObject('name', parameters('sku'), 'capacity', tryGet(parameters('sku'), 'capacity'), 'tier', tryGet(parameters('sku'), 'tier'), 'size', tryGet(parameters('sku'), 'size'), 'family', tryGet(parameters('sku'), 'family')))]", + "dependsOn": [ + "cognitiveService" + ] + }, + "cognitiveService_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.CognitiveServices/accounts/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "cognitiveService" + ] + }, + "cognitiveService_diagnosticSettings": { + "copy": { + "name": "cognitiveService_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.CognitiveServices/accounts/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null + } + }, + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "cognitiveService" + ] + }, + "cognitiveService_roleAssignments": { + "copy": { + "name": "cognitiveService_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.CognitiveServices/accounts/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.CognitiveServices/accounts', 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": [ + "cognitiveService" + ] + }, + "cognitiveService_privateEndpoints": { + "copy": { + "name": "cognitiveService_privateEndpoints", + "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-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]]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'name'), format('pep-{0}-{1}-{2}', last(split(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'account'), copyIndex()))]" + }, + "privateLinkServiceConnections": "[if(not(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true())), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'account'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'account')))))), createObject('value', null()))]", + "manualPrivateLinkServiceConnections": "[if(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true()), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'account'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'account')), 'requestMessage', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualConnectionRequestMessage'), 'Manual approval required.'))))), createObject('value', null()))]", + "subnetResourceId": { + "value": "[coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId]" + }, + "enableTelemetry": { + "value": "[variables('enableReferencedModulesTelemetry')]" + }, + "location": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'location'), reference(split(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location)]" + }, + "lock": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" + }, + "privateDnsZoneGroup": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroup')]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'roleAssignments')]" + }, + "tags": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + }, + "customDnsConfigs": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customDnsConfigs')]" + }, + "ipConfigurations": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'ipConfigurations')]" + }, + "applicationSecurityGroupResourceIds": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'applicationSecurityGroupResourceIds')]" + }, + "customNetworkInterfaceName": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customNetworkInterfaceName')]" + } + }, + "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.34.44.8038", + "templateHash": "12389807800450456797" + }, + "name": "Private Endpoints", + "description": "This module deploys a Private Endpoint." + }, + "definitions": { + "privateDnsZoneGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, + "metadata": { + "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "ipConfigurationType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource that is unique within a resource group." + } + }, + "properties": { + "type": "object", + "properties": { + "groupId": { + "type": "string", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } + } + }, + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "privateLinkServiceConnectionType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the private link service connection." + } + }, + "properties": { + "type": "object", + "properties": { + "groupIds": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." + } + }, + "privateLinkServiceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of private link service." + } + }, + "requestMessage": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." + } + } + }, + "metadata": { + "description": "Required. Properties of private link service connection." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "customDnsConfigType": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. FQDN that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "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." + } + } + }, + "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.5.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": { + "__bicep_imported_from!": { + "sourceTemplate": "private-dns-zone-group/main.bicep" + } + } + }, + "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.5.1" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the private endpoint resource to create." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + } + }, + "applicationSecurityGroupResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Application security groups in which the private endpoint IP configuration is included." + } + }, + "customNetworkInterfaceName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The custom name of the network interface attached to the private endpoint." + } + }, + "ipConfigurations": { + "type": "array", + "items": { + "$ref": "#/definitions/ipConfigurationType" + }, + "nullable": true, + "metadata": { + "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." + } + }, + "privateDnsZoneGroup": { + "$ref": "#/definitions/privateDnsZoneGroupType", + "nullable": true, + "metadata": { + "description": "Optional. The private DNS zone group to configure for the private endpoint." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "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", + "nullable": true, + "metadata": { + "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/customDnsConfigType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Custom DNS configurations." + } + }, + "manualPrivateLinkServiceConnections": { + "type": "array", + "items": { + "$ref": "#/definitions/privateLinkServiceConnectionType" + }, + "nullable": true, + "metadata": { + "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." + } + }, + "privateLinkServiceConnections": { + "type": "array", + "items": { + "$ref": "#/definitions/privateLinkServiceConnectionType" + }, + "nullable": true, + "metadata": { + "description": "Conditional. A grouping of information about the connection to the remote resource. Required if `manualPrivateLinkServiceConnections` is empty." + } + }, + "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": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.11.0', '.', '-'), 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-05-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": "[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": "2022-09-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.34.44.8038", + "templateHash": "13997305779829540948" + }, + "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 + } + } + }, + "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." + } + } + }, + "variables": { + "copy": [ + { + "name": "privateDnsZoneConfigsVar", + "count": "[length(parameters('privateDnsZoneConfigs'))]", + "input": { + "name": "[coalesce(tryGet(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')], 'name'), last(split(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId, '/')))]", + "properties": { + "privateDnsZoneId": "[parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId]" + } + } + } + ] + }, + "resources": { + "privateEndpoint": { + "existing": true, + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2024-05-01", + "name": "[parameters('privateEndpointName')]" + }, + "privateDnsZoneGroup": { + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2024-05-01", + "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", + "properties": { + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" + } + } + }, + "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-05-01', 'full').location]" + }, + "customDnsConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/customDnsConfigType" + }, + "metadata": { + "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": [ + "cognitiveService" + ] + }, + "secretsExport": { + "condition": "[not(equals(parameters('secretsExportConfiguration'), null()))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-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]]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "keyVaultName": { + "value": "[last(split(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '/'))]" + }, + "secretsToSet": { + "value": "[union(createArray(), if(contains(parameters('secretsExportConfiguration'), 'accessKey1Name'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'accessKey1Name'), 'value', listKeys('cognitiveService', '2025-04-01-preview').key1)), createArray()), if(contains(parameters('secretsExportConfiguration'), 'accessKey2Name'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'accessKey2Name'), 'value', listKeys('cognitiveService', '2025-04-01-preview').key2)), createArray()))]" + } + }, + "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.1.42791", + "templateHash": "1200612323329026557" + } + }, + "definitions": { + "secretSetOutputType": { + "type": "object", + "properties": { + "secretResourceId": { + "type": "string", + "metadata": { + "description": "The resourceId of the exported secret." + } + }, + "secretUri": { + "type": "string", + "metadata": { + "description": "The secret URI of the exported secret." + } + }, + "secretUriWithVersion": { + "type": "string", + "metadata": { + "description": "The secret URI with version of the exported secret." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for the output of the secret set via the secrets export feature.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "secretToSetType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the secret to set." + } + }, + "value": { + "type": "securestring", + "metadata": { + "description": "Required. The value of the secret to set." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for the secret to set via the secrets export feature.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + } + }, + "parameters": { + "keyVaultName": { + "type": "string", + "metadata": { + "description": "Required. The name of the Key Vault to set the ecrets in." + } + }, + "secretsToSet": { + "type": "array", + "items": { + "$ref": "#/definitions/secretToSetType" + }, + "metadata": { + "description": "Required. The secrets to set in the Key Vault." + } + } + }, + "resources": { + "keyVault": { + "existing": true, + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2023-07-01", + "name": "[parameters('keyVaultName')]" + }, + "secrets": { + "copy": { + "name": "secrets", + "count": "[length(parameters('secretsToSet'))]" + }, + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2023-07-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('secretsToSet')[copyIndex()].name)]", + "properties": { + "value": "[parameters('secretsToSet')[copyIndex()].value]" + } + } + }, + "outputs": { + "secretsSet": { + "type": "array", + "items": { + "$ref": "#/definitions/secretSetOutputType" + }, + "metadata": { + "description": "The references to the secrets exported to the provided Key Vault." + }, + "copy": { + "count": "[length(range(0, length(coalesce(parameters('secretsToSet'), createArray()))))]", + "input": { + "secretResourceId": "[resourceId('Microsoft.KeyVault/vaults/secrets', parameters('keyVaultName'), parameters('secretsToSet')[range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()]].name)]", + "secretUri": "[reference(format('secrets[{0}]', range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()])).secretUri]", + "secretUriWithVersion": "[reference(format('secrets[{0}]', range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()])).secretUriWithVersion]" + } + } + } + } + } + }, + "dependsOn": [ + "cognitiveService" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the cognitive services account." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the cognitive services account." + }, + "value": "[resourceId('Microsoft.CognitiveServices/accounts', parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the cognitive services account was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "endpoint": { + "type": "string", + "metadata": { + "description": "The service endpoint of the cognitive services account." + }, + "value": "[reference('cognitiveService').endpoint]" + }, + "endpoints": { + "$ref": "#/definitions/endpointType", + "metadata": { + "description": "All endpoints available for the cognitive services account, types depends on the cognitive service kind." + }, + "value": "[reference('cognitiveService').endpoints]" + }, + "systemAssignedMIPrincipalId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "The principal ID of the system assigned identity." + }, + "value": "[tryGet(tryGet(reference('cognitiveService', '2025-04-01-preview', 'full'), 'identity'), 'principalId')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('cognitiveService', '2025-04-01-preview', 'full').location]" + }, + "exportedSecrets": { + "$ref": "#/definitions/secretsOutputType", + "metadata": { + "description": "A hashtable of references to the secrets exported to the provided Key Vault. The key of each reference is each secret's name." + }, + "value": "[if(not(equals(parameters('secretsExportConfiguration'), null())), toObject(reference('secretsExport').outputs.secretsSet.value, lambda('secret', last(split(lambdaVariables('secret').secretResourceId, '/'))), lambda('secret', lambdaVariables('secret'))), createObject())]" + }, + "privateEndpoints": { + "type": "array", + "items": { + "$ref": "#/definitions/privateEndpointOutputType" + }, + "metadata": { + "description": "The private endpoints of the congitive services account." + }, + "copy": { + "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]", + "input": { + "name": "[reference(format('cognitiveService_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", + "resourceId": "[reference(format('cognitiveService_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", + "groupId": "[tryGet(tryGet(reference(format('cognitiveService_privateEndpoints[{0}]', copyIndex())).outputs, 'groupId'), 'value')]", + "customDnsConfigs": "[reference(format('cognitiveService_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfigs.value]", + "networkInterfaceResourceIds": "[reference(format('cognitiveService_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceResourceIds.value]" + } + } + } + } + } + } + } + }, + "outputs": { + "resourceId": { + "type": "string", + "value": "[reference('cognitiveService').outputs.resourceId.value]" + }, + "name": { + "type": "string", + "value": "[reference('cognitiveService').outputs.name.value]" + }, + "systemAssignedMIPrincipalId": { + "type": "string", + "nullable": true, + "value": "[tryGet(tryGet(reference('cognitiveService').outputs, 'systemAssignedMIPrincipalId'), 'value')]" + }, + "endpoint": { + "type": "string", + "value": "[reference('cognitiveService').outputs.endpoint.value]" + }, + "foundryConnection": { + "type": "object", + "value": { + "name": "[reference('cognitiveService').outputs.name.value]", + "value": null, + "category": "[parameters('category')]", + "target": "[reference('cognitiveService').outputs.endpoint.value]", + "kind": "[parameters('kind')]", + "connectionProperties": { + "authType": "AAD" + }, + "isSharedToAll": true, + "metadata": { + "ApiType": "Azure", + "Kind": "[parameters('kind')]", + "ResourceId": "[reference('cognitiveService').outputs.resourceId.value]" + } + } + } + } + } + }, + "dependsOn": [ + "cognitiveServicesPrivateDnsZone" + ] + }, + "language": { + "condition": "[parameters('languageEnabled')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('{0}-language-deployment', parameters('name')), 64)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[format('lang{0}{1}', parameters('name'), parameters('resourceToken'))]" + }, + "location": { + "value": "[parameters('location')]" + }, + "kind": { + "value": "TextAnalytics" + }, + "sku": { + "value": "S" + }, + "networkIsolation": { + "value": "[parameters('networkIsolation')]" + }, + "networkAcls": { + "value": "[parameters('networkAcls')]" + }, + "virtualNetworkSubnetResourceId": "[if(parameters('networkIsolation'), createObject('value', parameters('virtualNetworkSubnetResourceId')), createObject('value', ''))]", + "privateDnsZonesResourceIds": "[if(parameters('networkIsolation'), createObject('value', createArray(reference('cognitiveServicesPrivateDnsZone').outputs.resourceId.value)), createObject('value', createArray()))]", + "logAnalyticsWorkspaceResourceId": { + "value": "[parameters('logAnalyticsWorkspaceResourceId')]" + }, + "roleAssignments": { + "value": "[variables('allRoleAssignments')]" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "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.1.42791", + "templateHash": "2348080591288311162" + } + }, + "definitions": { + "deploymentsType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of cognitive service account deployment." + } + }, + "model": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of Cognitive Services account deployment model." + } + }, + "format": { + "type": "string", + "metadata": { + "description": "Required. The format of Cognitive Services account deployment model." + } + }, + "version": { + "type": "string", + "metadata": { + "description": "Required. The version of Cognitive Services account deployment model." + } + } + }, + "metadata": { + "description": "Required. Properties of Cognitive Services account deployment model." + } + }, + "sku": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource model definition representing SKU." + } + }, + "capacity": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The capacity of the resource model definition representing SKU." + } + }, + "tier": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The tier of the resource model definition representing SKU." + } + }, + "size": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The size of the resource model definition representing SKU." + } + }, + "family": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The family of the resource model definition representing SKU." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The resource model definition representing SKU." + } + }, + "raiPolicyName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of RAI policy." + } + }, + "versionUpgradeOption": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The version upgrade option." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "../customTypes.bicep" + } + } + }, + "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.5.1" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Name of the Cognitive Services resource. Must be unique in the resource group." + } + }, + "location": { + "type": "string", + "metadata": { + "description": "The location of the Cognitive Services resource." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "AIServices", + "AnomalyDetector", + "CognitiveServices", + "ComputerVision", + "ContentModerator", + "ContentSafety", + "ConversationalLanguageUnderstanding", + "CustomVision.Prediction", + "CustomVision.Training", + "Face", + "FormRecognizer", + "HealthInsights", + "ImmersiveReader", + "Internal.AllInOne", + "LUIS", + "LUIS.Authoring", + "LanguageAuthoring", + "MetricsAdvisor", + "OpenAI", + "Personalizer", + "QnAMaker.v2", + "SpeechServices", + "TextAnalytics", + "TextTranslation" + ], + "metadata": { + "description": "Required. Kind of the Cognitive Services account. Use 'Get-AzCognitiveServicesAccountSku' to determine a valid combinations of 'kind' and 'SKU' for your Azure region." + } + }, + "sku": { + "type": "string", + "defaultValue": "S0", + "allowedValues": [ + "S", + "S0", + "S1", + "S2", + "S3", + "S4", + "S5", + "S6", + "S7", + "S8" + ], + "metadata": { + "description": "Required. The SKU of the Cognitive Services account. Use 'Get-AzCognitiveServicesAccountSku' to determine a valid combinations of 'kind' and 'SKU' for your Azure region." + } + }, + "category": { + "type": "string", + "defaultValue": "CognitiveService", + "metadata": { + "description": "Category of the Cognitive Services account." + } + }, + "networkIsolation": { + "type": "bool", + "metadata": { + "description": "Specifies whether to enable network isolation. If true, the resource will be deployed in a private endpoint and public network access will be disabled." + } + }, + "privateDnsZonesResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "defaultValue": [], + "metadata": { + "description": "Existing resource ID of the private DNS zone for the private endpoint." + } + }, + "virtualNetworkSubnetResourceId": { + "type": "string", + "metadata": { + "description": "Resource ID of the subnet for the private endpoint." + } + }, + "logAnalyticsWorkspaceResourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the Log Analytics workspace to use for diagnostic settings." + } + }, + "aiModelDeployments": { + "type": "array", + "items": { + "$ref": "#/definitions/deploymentsType" + }, + "defaultValue": [], + "metadata": { + "description": "Optional. Specifies the OpenAI deployments to create." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Tags to be applied to the resources." + } + }, + "networkAcls": { + "type": "object", + "metadata": { + "description": "Optional. A collection of rules governing the accessibility from specific network locations." + } + } + }, + "variables": { + "copy": [ + { + "name": "privateDnsZones", + "count": "[length(parameters('privateDnsZonesResourceIds'))]", + "input": { + "privateDnsZoneResourceId": "[parameters('privateDnsZonesResourceIds')[copyIndex('privateDnsZones')]]" + } + } + ], + "nameFormatted": "[take(toLower(parameters('name')), 24)]" + }, + "resources": { + "cognitiveService": { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('cog-{0}-{1}-deployment', parameters('kind'), parameters('name')), 64)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[variables('nameFormatted')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "sku": { + "value": "[parameters('sku')]" + }, + "kind": { + "value": "[parameters('kind')]" + }, + "allowProjectManagement": { + "value": true + }, + "managedIdentities": { + "value": { + "systemAssigned": true + } + }, + "deployments": { + "value": "[parameters('aiModelDeployments')]" + }, + "customSubDomainName": { + "value": "[parameters('name')]" + }, + "disableLocalAuth": { + "value": "[parameters('networkIsolation')]" + }, + "publicNetworkAccess": "[if(parameters('networkIsolation'), createObject('value', 'Disabled'), createObject('value', 'Enabled'))]", + "diagnosticSettings": { + "value": [ + { + "workspaceResourceId": "[parameters('logAnalyticsWorkspaceResourceId')]" + } + ] + }, + "roleAssignments": { + "value": "[parameters('roleAssignments')]" + }, + "networkAcls": { + "value": "[parameters('networkAcls')]" + }, + "privateEndpoints": "[if(parameters('networkIsolation'), createObject('value', createArray(createObject('privateDnsZoneGroup', createObject('privateDnsZoneGroupConfigs', variables('privateDnsZones')), 'subnetResourceId', parameters('virtualNetworkSubnetResourceId')))), createObject('value', createArray()))]" + }, + "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.1.42791", + "templateHash": "16135659971302525380" + }, + "name": "Cognitive Services", + "description": "This module deploys a Cognitive Service." + }, + "definitions": { + "privateEndpointOutputType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the private endpoint." + } + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the private endpoint." + } + }, + "groupId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "The group Id for the private endpoint Group." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "FQDN that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "A list of private IP addresses of the private endpoint." + } + } + } + }, + "metadata": { + "description": "The custom DNS configurations of the private endpoint." + } + }, + "networkInterfaceResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "The IDs of the network interfaces associated with the private endpoint." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for the private endpoint output." + } + }, + "deploymentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of cognitive service account deployment." + } + }, + "model": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of Cognitive Services account deployment model." + } + }, + "format": { + "type": "string", + "metadata": { + "description": "Required. The format of Cognitive Services account deployment model." + } + }, + "version": { + "type": "string", + "metadata": { + "description": "Required. The version of Cognitive Services account deployment model." + } + } + }, + "metadata": { + "description": "Required. Properties of Cognitive Services account deployment model." + } + }, + "sku": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource model definition representing SKU." + } + }, + "capacity": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The capacity of the resource model definition representing SKU." + } + }, + "tier": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The tier of the resource model definition representing SKU." + } + }, + "size": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The size of the resource model definition representing SKU." + } + }, + "family": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The family of the resource model definition representing SKU." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The resource model definition representing SKU." + } + }, + "raiPolicyName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of RAI policy." + } + }, + "versionUpgradeOption": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The version upgrade option." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for a cognitive services account deployment." + } + }, + "endpointType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Type of the endpoint." + } + }, + "endpoint": { + "type": "string", + "nullable": true, + "metadata": { + "description": "The endpoint URI." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for a cognitive services account endpoint." + } + }, + "secretsExportConfigurationType": { + "type": "object", + "properties": { + "keyVaultResourceId": { + "type": "string", + "metadata": { + "description": "Required. The key vault name where to store the keys and connection strings generated by the modules." + } + }, + "accessKey1Name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name for the accessKey1 secret to create." + } + }, + "accessKey2Name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name for the accessKey2 secret to create." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type of the secrets exported to the provided Key Vault." + } + }, + "_1.privateEndpointCustomDnsConfigType": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. FQDN that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "_1.privateEndpointIpConfigurationType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource that is unique within a resource group." + } + }, + "properties": { + "type": "object", + "properties": { + "groupId": { + "type": "string", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } + } + }, + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "_1.privateEndpointPrivateDnsZoneGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "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": "Required. The private DNS Zone Groups to associate the Private Endpoint. A DNS Zone Group can support up to 5 DNS zones." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "_1.secretSetOutputType": { + "type": "object", + "properties": { + "secretResourceId": { + "type": "string", + "metadata": { + "description": "The resourceId of the exported secret." + } + }, + "secretUri": { + "type": "string", + "metadata": { + "description": "The secret URI of the exported secret." + } + }, + "secretUriWithVersion": { + "type": "string", + "metadata": { + "description": "The secret URI with version of the exported secret." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for the output of the secret set via the secrets export feature.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "customerManagedKeyType": { + "type": "object", + "properties": { + "keyVaultResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of a key vault to reference a customer managed key for encryption from." + } + }, + "keyName": { + "type": "string", + "metadata": { + "description": "Required. The name of the customer managed key to use for encryption." + } + }, + "keyVersion": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The version of the customer managed key to reference for encryption. If not provided, the deployment will use the latest version available at deployment time." + } + }, + "userAssignedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a customer-managed key. To be used if the resource type does not support auto-rotation of the customer-managed key.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "diagnosticSettingFullType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "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 logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "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 both logs & metrics are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "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." + } + } + }, + "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.5.1" + } + } + }, + "managedIdentityAllType": { + "type": "object", + "properties": { + "systemAssigned": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enables system assigned managed identity on the resource." + } + }, + "userAssignedResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The resource ID(s) to assign to the resource. Required if a user assigned identity is used for encryption." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a managed identity configuration. To be used if both a system-assigned & user-assigned identities are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "privateEndpointSingleServiceType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private Endpoint." + } + }, + "location": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The location to deploy the Private Endpoint to." + } + }, + "privateLinkServiceConnectionName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private link connection to create." + } + }, + "service": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The subresource to deploy the Private Endpoint for. For example \"vault\" for a Key Vault Private Endpoint." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + } + }, + "resourceGroupResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the Resource Group the Private Endpoint will be created in. If not specified, the Resource Group of the provided Virtual Network Subnet is used." + } + }, + "privateDnsZoneGroup": { + "$ref": "#/definitions/_1.privateEndpointPrivateDnsZoneGroupType", + "nullable": true, + "metadata": { + "description": "Optional. The private DNS Zone Group to configure for the Private Endpoint." + } + }, + "isManualConnection": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. If Manual Private Link Connection is required." + } + }, + "manualConnectionRequestMessage": { + "type": "string", + "nullable": true, + "maxLength": 140, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with the manual connection request." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/_1.privateEndpointCustomDnsConfigType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Custom DNS configurations." + } + }, + "ipConfigurations": { + "type": "array", + "items": { + "$ref": "#/definitions/_1.privateEndpointIpConfigurationType" + }, + "nullable": true, + "metadata": { + "description": "Optional. A list of IP configurations of the Private Endpoint. This will be used to map to the first-party Service endpoints." + } + }, + "applicationSecurityGroupResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Application security groups in which the Private Endpoint IP configuration is included." + } + }, + "customNetworkInterfaceName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The custom name of the network interface attached to the Private Endpoint." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags to be applied on all resources/Resource Groups in this deployment." + } + }, + "enableTelemetry": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a private endpoint. To be used if the private endpoint's default service / groupId can be assumed (i.e., for services that only have one Private Endpoint type like 'vault' for key vault).", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "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.5.1" + } + } + }, + "secretsOutputType": { + "type": "object", + "properties": {}, + "additionalProperties": { + "$ref": "#/definitions/_1.secretSetOutputType", + "metadata": { + "description": "An exported secret's references." + } + }, + "metadata": { + "description": "A map of the exported secrets", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of Cognitive Services account." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "AIServices", + "AnomalyDetector", + "CognitiveServices", + "ComputerVision", + "ContentModerator", + "ContentSafety", + "ConversationalLanguageUnderstanding", + "CustomVision.Prediction", + "CustomVision.Training", + "Face", + "FormRecognizer", + "HealthInsights", + "ImmersiveReader", + "Internal.AllInOne", + "LUIS", + "LUIS.Authoring", + "LanguageAuthoring", + "MetricsAdvisor", + "OpenAI", + "Personalizer", + "QnAMaker.v2", + "SpeechServices", + "TextAnalytics", + "TextTranslation" + ], + "metadata": { + "description": "Required. Kind of the Cognitive Services account. Use 'Get-AzCognitiveServicesAccountSku' to determine a valid combinations of 'kind' and 'SKU' for your Azure region." + } + }, + "sku": { + "type": "string", + "defaultValue": "S0", + "allowedValues": [ + "C2", + "C3", + "C4", + "F0", + "F1", + "S", + "S0", + "S1", + "S10", + "S2", + "S3", + "S4", + "S5", + "S6", + "S7", + "S8", + "S9" + ], + "metadata": { + "description": "Optional. SKU of the Cognitive Services account. Use 'Get-AzCognitiveServicesAccountSku' to determine a valid combinations of 'kind' and 'SKU' for your Azure region." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all Resources." + } + }, + "diagnosticSettings": { + "type": "array", + "items": { + "$ref": "#/definitions/diagnosticSettingFullType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "publicNetworkAccess": { + "type": "string", + "nullable": true, + "allowedValues": [ + "Enabled", + "Disabled" + ], + "metadata": { + "description": "Optional. Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set and networkAcls are not set." + } + }, + "customSubDomainName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. Subdomain name used for token-based authentication. Required if 'networkAcls' or 'privateEndpoints' are set." + } + }, + "networkAcls": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. A collection of rules governing the accessibility from specific network locations." + } + }, + "privateEndpoints": { + "type": "array", + "items": { + "$ref": "#/definitions/privateEndpointSingleServiceType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible." + } + }, + "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", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "allowedFqdnList": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. List of allowed FQDN." + } + }, + "apiProperties": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The API properties for special APIs." + } + }, + "disableLocalAuth": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Allow only Azure AD authentication. Should be enabled for security reasons." + } + }, + "customerManagedKey": { + "$ref": "#/definitions/customerManagedKeyType", + "nullable": true, + "metadata": { + "description": "Optional. The customer managed key definition." + } + }, + "dynamicThrottlingEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. The flag to enable dynamic throttling." + } + }, + "migrationToken": { + "type": "securestring", + "nullable": true, + "metadata": { + "description": "Optional. Resource migration token." + } + }, + "restore": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Restore a soft-deleted cognitive service at deployment time. Will fail if no such soft-deleted resource exists." + } + }, + "restrictOutboundNetworkAccess": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Restrict outbound network access." + } + }, + "userOwnedStorage": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The storage accounts for this resource." + } + }, + "managedIdentities": { + "$ref": "#/definitions/managedIdentityAllType", + "nullable": true, + "metadata": { + "description": "Optional. The managed identity definition for this resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "deployments": { + "type": "array", + "items": { + "$ref": "#/definitions/deploymentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of deployments about cognitive service accounts to create." + } + }, + "secretsExportConfiguration": { + "$ref": "#/definitions/secretsExportConfigurationType", + "nullable": true, + "metadata": { + "description": "Optional. Key vault reference and secret settings for the module's secrets export." + } + }, + "allowProjectManagement": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable/Disable project management feature for AI Foundry." + } + } + }, + "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)))))]" + } + ], + "enableReferencedModulesTelemetry": false, + "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", + "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned, UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', null())), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", + "builtInRoleNames": { + "Cognitive Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '25fbc0a9-bd7c-42a3-aa1a-3b75d497ee68')]", + "Cognitive Services Custom Vision Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c1ff6cc2-c111-46fe-8896-e0ef812ad9f3')]", + "Cognitive Services Custom Vision Deployment": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5c4089e1-6d96-4d2f-b296-c1bc7137275f')]", + "Cognitive Services Custom Vision Labeler": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '88424f51-ebe7-446f-bc41-7fa16989e96c')]", + "Cognitive Services Custom Vision Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '93586559-c37d-4a6b-ba08-b9f0940c2d73')]", + "Cognitive Services Custom Vision Trainer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0a5ae4ab-0d65-4eeb-be61-29fc9b54394b')]", + "Cognitive Services Data Reader (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b59867f0-fa02-499b-be73-45a86b5b3e1c')]", + "Cognitive Services Face Recognizer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9894cab4-e18a-44aa-828b-cb588cd6f2d7')]", + "Cognitive Services Immersive Reader User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b2de6794-95db-4659-8781-7e080d3f2b9d')]", + "Cognitive Services Language Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f07febfe-79bc-46b1-8b37-790e26e6e498')]", + "Cognitive Services Language Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7628b7b8-a8b2-4cdc-b46f-e9b35248918e')]", + "Cognitive Services Language Writer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f2310ca1-dc64-4889-bb49-c8e0fa3d47a8')]", + "Cognitive Services LUIS Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f72c8140-2111-481c-87ff-72b910f6e3f8')]", + "Cognitive Services LUIS Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18e81cdc-4e98-4e29-a639-e7d10c5a6226')]", + "Cognitive Services LUIS Writer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6322a993-d5c9-4bed-b113-e49bbea25b27')]", + "Cognitive Services Metrics Advisor Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'cb43c632-a144-4ec5-977c-e80c4affc34a')]", + "Cognitive Services Metrics Advisor User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3b20f47b-3825-43cb-8114-4bd2201156a8')]", + "Cognitive Services OpenAI Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a001fd3d-188f-4b5d-821b-7da978bf7442')]", + "Cognitive Services OpenAI User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e0bd9bd-7b93-4f28-af87-19fc36ad61bd')]", + "Cognitive Services QnA Maker Editor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f4cc2bf9-21be-47a1-bdf1-5c5804381025')]", + "Cognitive Services QnA Maker Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '466ccd10-b268-4a11-b098-b4849f024126')]", + "Cognitive Services Speech Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0e75ca1e-0464-4b4d-8b93-68208a576181')]", + "Cognitive Services Speech User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f2dc8367-1007-4938-bd23-fe263f013447')]", + "Cognitive Services User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a97b65f3-24c7-4388-baec-2e87135dc908')]", + "Azure AI Developer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '64702f94-c441-49e6-a78b-ef80e0188fee')]", + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "cMKKeyVault::cMKKey": { + "condition": "[and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), not(empty(tryGet(parameters('customerManagedKey'), 'keyName')))))]", + "existing": true, + "type": "Microsoft.KeyVault/vaults/keys", + "apiVersion": "2023-07-01", + "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[2]]", + "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[4]]", + "name": "[format('{0}/{1}', last(split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')), tryGet(parameters('customerManagedKey'), 'keyName'))]" + }, + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.cognitiveservices-account.{0}.{1}', replace('0.11.0', '.', '-'), 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" + } + } + } + } + }, + "cMKKeyVault": { + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId')))]", + "existing": true, + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2023-07-01", + "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[2]]", + "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[4]]", + "name": "[last(split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/'))]" + }, + "cMKUserAssignedIdentity": { + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId')))]", + "existing": true, + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2025-01-31-preview", + "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/')[2]]", + "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/')[4]]", + "name": "[last(split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/'))]" + }, + "cognitiveService": { + "type": "Microsoft.CognitiveServices/accounts", + "apiVersion": "2025-04-01-preview", + "name": "[parameters('name')]", + "kind": "[parameters('kind')]", + "identity": "[variables('identity')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "sku": { + "name": "[parameters('sku')]" + }, + "properties": { + "allowProjectManagement": "[parameters('allowProjectManagement')]", + "customSubDomainName": "[parameters('customSubDomainName')]", + "networkAcls": "[if(not(empty(coalesce(parameters('networkAcls'), createObject()))), createObject('defaultAction', tryGet(parameters('networkAcls'), 'defaultAction'), 'virtualNetworkRules', coalesce(tryGet(parameters('networkAcls'), 'virtualNetworkRules'), createArray()), 'ipRules', coalesce(tryGet(parameters('networkAcls'), 'ipRules'), createArray())), null())]", + "publicNetworkAccess": "[if(not(equals(parameters('publicNetworkAccess'), null())), parameters('publicNetworkAccess'), if(not(empty(parameters('networkAcls'))), 'Enabled', 'Disabled'))]", + "allowedFqdnList": "[parameters('allowedFqdnList')]", + "apiProperties": "[parameters('apiProperties')]", + "disableLocalAuth": "[parameters('disableLocalAuth')]", + "encryption": "[if(not(empty(parameters('customerManagedKey'))), createObject('keySource', 'Microsoft.KeyVault', 'keyVaultProperties', createObject('identityClientId', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), ''))), reference('cMKUserAssignedIdentity').clientId, null()), 'keyVaultUri', reference('cMKKeyVault').vaultUri, 'keyName', parameters('customerManagedKey').keyName, 'keyVersion', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'keyVersion'), ''))), tryGet(parameters('customerManagedKey'), 'keyVersion'), last(split(reference('cMKKeyVault::cMKKey').keyUriWithVersion, '/'))))), null())]", + "migrationToken": "[parameters('migrationToken')]", + "restore": "[parameters('restore')]", + "restrictOutboundNetworkAccess": "[parameters('restrictOutboundNetworkAccess')]", + "userOwnedStorage": "[parameters('userOwnedStorage')]", + "dynamicThrottlingEnabled": "[parameters('dynamicThrottlingEnabled')]" + }, + "dependsOn": [ + "cMKKeyVault", + "cMKKeyVault::cMKKey", + "cMKUserAssignedIdentity" + ] + }, + "cognitiveService_deployments": { + "copy": { + "name": "cognitiveService_deployments", + "count": "[length(coalesce(parameters('deployments'), createArray()))]", + "mode": "serial", + "batchSize": 1 + }, + "type": "Microsoft.CognitiveServices/accounts/deployments", + "apiVersion": "2025-04-01-preview", + "name": "[format('{0}/{1}', parameters('name'), coalesce(tryGet(coalesce(parameters('deployments'), createArray())[copyIndex()], 'name'), format('{0}-deployments', parameters('name'))))]", + "properties": { + "model": "[coalesce(parameters('deployments'), createArray())[copyIndex()].model]", + "raiPolicyName": "[tryGet(coalesce(parameters('deployments'), createArray())[copyIndex()], 'raiPolicyName')]", + "versionUpgradeOption": "[tryGet(coalesce(parameters('deployments'), createArray())[copyIndex()], 'versionUpgradeOption')]" + }, + "sku": "[coalesce(tryGet(coalesce(parameters('deployments'), createArray())[copyIndex()], 'sku'), createObject('name', parameters('sku'), 'capacity', tryGet(parameters('sku'), 'capacity'), 'tier', tryGet(parameters('sku'), 'tier'), 'size', tryGet(parameters('sku'), 'size'), 'family', tryGet(parameters('sku'), 'family')))]", + "dependsOn": [ + "cognitiveService" + ] + }, + "cognitiveService_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.CognitiveServices/accounts/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "cognitiveService" + ] + }, + "cognitiveService_diagnosticSettings": { + "copy": { + "name": "cognitiveService_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.CognitiveServices/accounts/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null + } + }, + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "cognitiveService" + ] + }, + "cognitiveService_roleAssignments": { + "copy": { + "name": "cognitiveService_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.CognitiveServices/accounts/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.CognitiveServices/accounts', 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": [ + "cognitiveService" + ] + }, + "cognitiveService_privateEndpoints": { + "copy": { + "name": "cognitiveService_privateEndpoints", + "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-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]]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'name'), format('pep-{0}-{1}-{2}', last(split(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'account'), copyIndex()))]" + }, + "privateLinkServiceConnections": "[if(not(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true())), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'account'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'account')))))), createObject('value', null()))]", + "manualPrivateLinkServiceConnections": "[if(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true()), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'account'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'account')), 'requestMessage', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualConnectionRequestMessage'), 'Manual approval required.'))))), createObject('value', null()))]", + "subnetResourceId": { + "value": "[coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId]" + }, + "enableTelemetry": { + "value": "[variables('enableReferencedModulesTelemetry')]" + }, + "location": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'location'), reference(split(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location)]" + }, + "lock": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" + }, + "privateDnsZoneGroup": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroup')]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'roleAssignments')]" + }, + "tags": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + }, + "customDnsConfigs": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customDnsConfigs')]" + }, + "ipConfigurations": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'ipConfigurations')]" + }, + "applicationSecurityGroupResourceIds": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'applicationSecurityGroupResourceIds')]" + }, + "customNetworkInterfaceName": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customNetworkInterfaceName')]" + } + }, + "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.34.44.8038", + "templateHash": "12389807800450456797" + }, + "name": "Private Endpoints", + "description": "This module deploys a Private Endpoint." + }, + "definitions": { + "privateDnsZoneGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, + "metadata": { + "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "ipConfigurationType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource that is unique within a resource group." + } + }, + "properties": { + "type": "object", + "properties": { + "groupId": { + "type": "string", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } + } + }, + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "privateLinkServiceConnectionType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the private link service connection." + } + }, + "properties": { + "type": "object", + "properties": { + "groupIds": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." + } + }, + "privateLinkServiceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of private link service." + } + }, + "requestMessage": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." + } + } + }, + "metadata": { + "description": "Required. Properties of private link service connection." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "customDnsConfigType": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. FQDN that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "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." + } + } + }, + "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.5.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": { + "__bicep_imported_from!": { + "sourceTemplate": "private-dns-zone-group/main.bicep" + } + } + }, + "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.5.1" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the private endpoint resource to create." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + } + }, + "applicationSecurityGroupResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Application security groups in which the private endpoint IP configuration is included." + } + }, + "customNetworkInterfaceName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The custom name of the network interface attached to the private endpoint." + } + }, + "ipConfigurations": { + "type": "array", + "items": { + "$ref": "#/definitions/ipConfigurationType" + }, + "nullable": true, + "metadata": { + "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." + } + }, + "privateDnsZoneGroup": { + "$ref": "#/definitions/privateDnsZoneGroupType", + "nullable": true, + "metadata": { + "description": "Optional. The private DNS zone group to configure for the private endpoint." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "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", + "nullable": true, + "metadata": { + "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/customDnsConfigType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Custom DNS configurations." + } + }, + "manualPrivateLinkServiceConnections": { + "type": "array", + "items": { + "$ref": "#/definitions/privateLinkServiceConnectionType" + }, + "nullable": true, + "metadata": { + "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." + } + }, + "privateLinkServiceConnections": { + "type": "array", + "items": { + "$ref": "#/definitions/privateLinkServiceConnectionType" + }, + "nullable": true, + "metadata": { + "description": "Conditional. A grouping of information about the connection to the remote resource. Required if `manualPrivateLinkServiceConnections` is empty." + } + }, + "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": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.11.0', '.', '-'), 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-05-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": "[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": "2022-09-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.34.44.8038", + "templateHash": "13997305779829540948" + }, + "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 + } + } + }, + "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." + } + } + }, + "variables": { + "copy": [ + { + "name": "privateDnsZoneConfigsVar", + "count": "[length(parameters('privateDnsZoneConfigs'))]", + "input": { + "name": "[coalesce(tryGet(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')], 'name'), last(split(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId, '/')))]", + "properties": { + "privateDnsZoneId": "[parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId]" + } + } + } + ] + }, + "resources": { + "privateEndpoint": { + "existing": true, + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2024-05-01", + "name": "[parameters('privateEndpointName')]" + }, + "privateDnsZoneGroup": { + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2024-05-01", + "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", + "properties": { + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" + } + } + }, + "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-05-01', 'full').location]" + }, + "customDnsConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/customDnsConfigType" + }, + "metadata": { + "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": [ + "cognitiveService" + ] + }, + "secretsExport": { + "condition": "[not(equals(parameters('secretsExportConfiguration'), null()))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-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]]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "keyVaultName": { + "value": "[last(split(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '/'))]" + }, + "secretsToSet": { + "value": "[union(createArray(), if(contains(parameters('secretsExportConfiguration'), 'accessKey1Name'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'accessKey1Name'), 'value', listKeys('cognitiveService', '2025-04-01-preview').key1)), createArray()), if(contains(parameters('secretsExportConfiguration'), 'accessKey2Name'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'accessKey2Name'), 'value', listKeys('cognitiveService', '2025-04-01-preview').key2)), createArray()))]" + } + }, + "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.1.42791", + "templateHash": "1200612323329026557" + } + }, + "definitions": { + "secretSetOutputType": { + "type": "object", + "properties": { + "secretResourceId": { + "type": "string", + "metadata": { + "description": "The resourceId of the exported secret." + } + }, + "secretUri": { + "type": "string", + "metadata": { + "description": "The secret URI of the exported secret." + } + }, + "secretUriWithVersion": { + "type": "string", + "metadata": { + "description": "The secret URI with version of the exported secret." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for the output of the secret set via the secrets export feature.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "secretToSetType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the secret to set." + } + }, + "value": { + "type": "securestring", + "metadata": { + "description": "Required. The value of the secret to set." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for the secret to set via the secrets export feature.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + } + }, + "parameters": { + "keyVaultName": { + "type": "string", + "metadata": { + "description": "Required. The name of the Key Vault to set the ecrets in." + } + }, + "secretsToSet": { + "type": "array", + "items": { + "$ref": "#/definitions/secretToSetType" + }, + "metadata": { + "description": "Required. The secrets to set in the Key Vault." + } + } + }, + "resources": { + "keyVault": { + "existing": true, + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2023-07-01", + "name": "[parameters('keyVaultName')]" + }, + "secrets": { + "copy": { + "name": "secrets", + "count": "[length(parameters('secretsToSet'))]" + }, + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2023-07-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('secretsToSet')[copyIndex()].name)]", + "properties": { + "value": "[parameters('secretsToSet')[copyIndex()].value]" + } + } + }, + "outputs": { + "secretsSet": { + "type": "array", + "items": { + "$ref": "#/definitions/secretSetOutputType" + }, + "metadata": { + "description": "The references to the secrets exported to the provided Key Vault." + }, + "copy": { + "count": "[length(range(0, length(coalesce(parameters('secretsToSet'), createArray()))))]", + "input": { + "secretResourceId": "[resourceId('Microsoft.KeyVault/vaults/secrets', parameters('keyVaultName'), parameters('secretsToSet')[range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()]].name)]", + "secretUri": "[reference(format('secrets[{0}]', range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()])).secretUri]", + "secretUriWithVersion": "[reference(format('secrets[{0}]', range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()])).secretUriWithVersion]" + } + } + } + } + } + }, + "dependsOn": [ + "cognitiveService" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the cognitive services account." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the cognitive services account." + }, + "value": "[resourceId('Microsoft.CognitiveServices/accounts', parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the cognitive services account was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "endpoint": { + "type": "string", + "metadata": { + "description": "The service endpoint of the cognitive services account." + }, + "value": "[reference('cognitiveService').endpoint]" + }, + "endpoints": { + "$ref": "#/definitions/endpointType", + "metadata": { + "description": "All endpoints available for the cognitive services account, types depends on the cognitive service kind." + }, + "value": "[reference('cognitiveService').endpoints]" + }, + "systemAssignedMIPrincipalId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "The principal ID of the system assigned identity." + }, + "value": "[tryGet(tryGet(reference('cognitiveService', '2025-04-01-preview', 'full'), 'identity'), 'principalId')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('cognitiveService', '2025-04-01-preview', 'full').location]" + }, + "exportedSecrets": { + "$ref": "#/definitions/secretsOutputType", + "metadata": { + "description": "A hashtable of references to the secrets exported to the provided Key Vault. The key of each reference is each secret's name." + }, + "value": "[if(not(equals(parameters('secretsExportConfiguration'), null())), toObject(reference('secretsExport').outputs.secretsSet.value, lambda('secret', last(split(lambdaVariables('secret').secretResourceId, '/'))), lambda('secret', lambdaVariables('secret'))), createObject())]" + }, + "privateEndpoints": { + "type": "array", + "items": { + "$ref": "#/definitions/privateEndpointOutputType" + }, + "metadata": { + "description": "The private endpoints of the congitive services account." + }, + "copy": { + "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]", + "input": { + "name": "[reference(format('cognitiveService_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", + "resourceId": "[reference(format('cognitiveService_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", + "groupId": "[tryGet(tryGet(reference(format('cognitiveService_privateEndpoints[{0}]', copyIndex())).outputs, 'groupId'), 'value')]", + "customDnsConfigs": "[reference(format('cognitiveService_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfigs.value]", + "networkInterfaceResourceIds": "[reference(format('cognitiveService_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceResourceIds.value]" + } + } + } + } + } + } + } + }, + "outputs": { + "resourceId": { + "type": "string", + "value": "[reference('cognitiveService').outputs.resourceId.value]" + }, + "name": { + "type": "string", + "value": "[reference('cognitiveService').outputs.name.value]" + }, + "systemAssignedMIPrincipalId": { + "type": "string", + "nullable": true, + "value": "[tryGet(tryGet(reference('cognitiveService').outputs, 'systemAssignedMIPrincipalId'), 'value')]" + }, + "endpoint": { + "type": "string", + "value": "[reference('cognitiveService').outputs.endpoint.value]" + }, + "foundryConnection": { + "type": "object", + "value": { + "name": "[reference('cognitiveService').outputs.name.value]", + "value": null, + "category": "[parameters('category')]", + "target": "[reference('cognitiveService').outputs.endpoint.value]", + "kind": "[parameters('kind')]", + "connectionProperties": { + "authType": "AAD" + }, + "isSharedToAll": true, + "metadata": { + "ApiType": "Azure", + "Kind": "[parameters('kind')]", + "ResourceId": "[reference('cognitiveService').outputs.resourceId.value]" + } + } + } + } + } + }, + "dependsOn": [ + "cognitiveServicesPrivateDnsZone" + ] + }, + "speech": { + "condition": "[parameters('speechEnabled')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('{0}-speech-deployment', parameters('name')), 64)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[format('speech{0}{1}', parameters('name'), parameters('resourceToken'))]" + }, + "location": { + "value": "[parameters('location')]" + }, + "kind": { + "value": "SpeechServices" + }, + "networkIsolation": { + "value": "[parameters('networkIsolation')]" + }, + "networkAcls": { + "value": "[parameters('networkAcls')]" + }, + "virtualNetworkSubnetResourceId": "[if(parameters('networkIsolation'), createObject('value', parameters('virtualNetworkSubnetResourceId')), createObject('value', ''))]", + "privateDnsZonesResourceIds": "[if(parameters('networkIsolation'), createObject('value', createArray(reference('cognitiveServicesPrivateDnsZone').outputs.resourceId.value)), createObject('value', createArray()))]", + "logAnalyticsWorkspaceResourceId": { + "value": "[parameters('logAnalyticsWorkspaceResourceId')]" + }, + "roleAssignments": { + "value": "[variables('allRoleAssignments')]" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "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.1.42791", + "templateHash": "2348080591288311162" + } + }, + "definitions": { + "deploymentsType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of cognitive service account deployment." + } + }, + "model": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of Cognitive Services account deployment model." + } + }, + "format": { + "type": "string", + "metadata": { + "description": "Required. The format of Cognitive Services account deployment model." + } + }, + "version": { + "type": "string", + "metadata": { + "description": "Required. The version of Cognitive Services account deployment model." + } + } + }, + "metadata": { + "description": "Required. Properties of Cognitive Services account deployment model." + } + }, + "sku": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource model definition representing SKU." + } + }, + "capacity": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The capacity of the resource model definition representing SKU." + } + }, + "tier": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The tier of the resource model definition representing SKU." + } + }, + "size": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The size of the resource model definition representing SKU." + } + }, + "family": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The family of the resource model definition representing SKU." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The resource model definition representing SKU." + } + }, + "raiPolicyName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of RAI policy." + } + }, + "versionUpgradeOption": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The version upgrade option." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "../customTypes.bicep" + } + } + }, + "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.5.1" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Name of the Cognitive Services resource. Must be unique in the resource group." + } + }, + "location": { + "type": "string", + "metadata": { + "description": "The location of the Cognitive Services resource." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "AIServices", + "AnomalyDetector", + "CognitiveServices", + "ComputerVision", + "ContentModerator", + "ContentSafety", + "ConversationalLanguageUnderstanding", + "CustomVision.Prediction", + "CustomVision.Training", + "Face", + "FormRecognizer", + "HealthInsights", + "ImmersiveReader", + "Internal.AllInOne", + "LUIS", + "LUIS.Authoring", + "LanguageAuthoring", + "MetricsAdvisor", + "OpenAI", + "Personalizer", + "QnAMaker.v2", + "SpeechServices", + "TextAnalytics", + "TextTranslation" + ], + "metadata": { + "description": "Required. Kind of the Cognitive Services account. Use 'Get-AzCognitiveServicesAccountSku' to determine a valid combinations of 'kind' and 'SKU' for your Azure region." + } + }, + "sku": { + "type": "string", + "defaultValue": "S0", + "allowedValues": [ + "S", + "S0", + "S1", + "S2", + "S3", + "S4", + "S5", + "S6", + "S7", + "S8" + ], + "metadata": { + "description": "Required. The SKU of the Cognitive Services account. Use 'Get-AzCognitiveServicesAccountSku' to determine a valid combinations of 'kind' and 'SKU' for your Azure region." + } + }, + "category": { + "type": "string", + "defaultValue": "CognitiveService", + "metadata": { + "description": "Category of the Cognitive Services account." + } + }, + "networkIsolation": { + "type": "bool", + "metadata": { + "description": "Specifies whether to enable network isolation. If true, the resource will be deployed in a private endpoint and public network access will be disabled." + } + }, + "privateDnsZonesResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "defaultValue": [], + "metadata": { + "description": "Existing resource ID of the private DNS zone for the private endpoint." + } + }, + "virtualNetworkSubnetResourceId": { + "type": "string", + "metadata": { + "description": "Resource ID of the subnet for the private endpoint." + } + }, + "logAnalyticsWorkspaceResourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the Log Analytics workspace to use for diagnostic settings." + } + }, + "aiModelDeployments": { + "type": "array", + "items": { + "$ref": "#/definitions/deploymentsType" + }, + "defaultValue": [], + "metadata": { + "description": "Optional. Specifies the OpenAI deployments to create." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Tags to be applied to the resources." + } + }, + "networkAcls": { + "type": "object", + "metadata": { + "description": "Optional. A collection of rules governing the accessibility from specific network locations." + } + } + }, + "variables": { + "copy": [ + { + "name": "privateDnsZones", + "count": "[length(parameters('privateDnsZonesResourceIds'))]", + "input": { + "privateDnsZoneResourceId": "[parameters('privateDnsZonesResourceIds')[copyIndex('privateDnsZones')]]" + } + } + ], + "nameFormatted": "[take(toLower(parameters('name')), 24)]" + }, + "resources": { + "cognitiveService": { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('cog-{0}-{1}-deployment', parameters('kind'), parameters('name')), 64)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[variables('nameFormatted')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "sku": { + "value": "[parameters('sku')]" + }, + "kind": { + "value": "[parameters('kind')]" + }, + "allowProjectManagement": { + "value": true + }, + "managedIdentities": { + "value": { + "systemAssigned": true + } + }, + "deployments": { + "value": "[parameters('aiModelDeployments')]" + }, + "customSubDomainName": { + "value": "[parameters('name')]" + }, + "disableLocalAuth": { + "value": "[parameters('networkIsolation')]" + }, + "publicNetworkAccess": "[if(parameters('networkIsolation'), createObject('value', 'Disabled'), createObject('value', 'Enabled'))]", + "diagnosticSettings": { + "value": [ + { + "workspaceResourceId": "[parameters('logAnalyticsWorkspaceResourceId')]" + } + ] + }, + "roleAssignments": { + "value": "[parameters('roleAssignments')]" + }, + "networkAcls": { + "value": "[parameters('networkAcls')]" + }, + "privateEndpoints": "[if(parameters('networkIsolation'), createObject('value', createArray(createObject('privateDnsZoneGroup', createObject('privateDnsZoneGroupConfigs', variables('privateDnsZones')), 'subnetResourceId', parameters('virtualNetworkSubnetResourceId')))), createObject('value', createArray()))]" + }, + "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.1.42791", + "templateHash": "16135659971302525380" + }, + "name": "Cognitive Services", + "description": "This module deploys a Cognitive Service." + }, + "definitions": { + "privateEndpointOutputType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the private endpoint." + } + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the private endpoint." + } + }, + "groupId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "The group Id for the private endpoint Group." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "FQDN that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "A list of private IP addresses of the private endpoint." + } + } + } + }, + "metadata": { + "description": "The custom DNS configurations of the private endpoint." + } + }, + "networkInterfaceResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "The IDs of the network interfaces associated with the private endpoint." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for the private endpoint output." + } + }, + "deploymentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of cognitive service account deployment." + } + }, + "model": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of Cognitive Services account deployment model." + } + }, + "format": { + "type": "string", + "metadata": { + "description": "Required. The format of Cognitive Services account deployment model." + } + }, + "version": { + "type": "string", + "metadata": { + "description": "Required. The version of Cognitive Services account deployment model." + } + } + }, + "metadata": { + "description": "Required. Properties of Cognitive Services account deployment model." + } + }, + "sku": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource model definition representing SKU." + } + }, + "capacity": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The capacity of the resource model definition representing SKU." + } + }, + "tier": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The tier of the resource model definition representing SKU." + } + }, + "size": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The size of the resource model definition representing SKU." + } + }, + "family": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The family of the resource model definition representing SKU." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The resource model definition representing SKU." + } + }, + "raiPolicyName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of RAI policy." + } + }, + "versionUpgradeOption": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The version upgrade option." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for a cognitive services account deployment." + } + }, + "endpointType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Type of the endpoint." + } + }, + "endpoint": { + "type": "string", + "nullable": true, + "metadata": { + "description": "The endpoint URI." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for a cognitive services account endpoint." + } + }, + "secretsExportConfigurationType": { + "type": "object", + "properties": { + "keyVaultResourceId": { + "type": "string", + "metadata": { + "description": "Required. The key vault name where to store the keys and connection strings generated by the modules." + } + }, + "accessKey1Name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name for the accessKey1 secret to create." + } + }, + "accessKey2Name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name for the accessKey2 secret to create." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type of the secrets exported to the provided Key Vault." + } + }, + "_1.privateEndpointCustomDnsConfigType": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. FQDN that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "_1.privateEndpointIpConfigurationType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource that is unique within a resource group." + } + }, + "properties": { + "type": "object", + "properties": { + "groupId": { + "type": "string", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } + } + }, + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "_1.privateEndpointPrivateDnsZoneGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "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": "Required. The private DNS Zone Groups to associate the Private Endpoint. A DNS Zone Group can support up to 5 DNS zones." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "_1.secretSetOutputType": { + "type": "object", + "properties": { + "secretResourceId": { + "type": "string", + "metadata": { + "description": "The resourceId of the exported secret." + } + }, + "secretUri": { + "type": "string", + "metadata": { + "description": "The secret URI of the exported secret." + } + }, + "secretUriWithVersion": { + "type": "string", + "metadata": { + "description": "The secret URI with version of the exported secret." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for the output of the secret set via the secrets export feature.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "customerManagedKeyType": { + "type": "object", + "properties": { + "keyVaultResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of a key vault to reference a customer managed key for encryption from." + } + }, + "keyName": { + "type": "string", + "metadata": { + "description": "Required. The name of the customer managed key to use for encryption." + } + }, + "keyVersion": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The version of the customer managed key to reference for encryption. If not provided, the deployment will use the latest version available at deployment time." + } + }, + "userAssignedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a customer-managed key. To be used if the resource type does not support auto-rotation of the customer-managed key.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "diagnosticSettingFullType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "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 logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "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 both logs & metrics are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "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." + } + } + }, + "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.5.1" + } + } + }, + "managedIdentityAllType": { + "type": "object", + "properties": { + "systemAssigned": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enables system assigned managed identity on the resource." + } + }, + "userAssignedResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The resource ID(s) to assign to the resource. Required if a user assigned identity is used for encryption." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a managed identity configuration. To be used if both a system-assigned & user-assigned identities are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "privateEndpointSingleServiceType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private Endpoint." + } + }, + "location": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The location to deploy the Private Endpoint to." + } + }, + "privateLinkServiceConnectionName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private link connection to create." + } + }, + "service": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The subresource to deploy the Private Endpoint for. For example \"vault\" for a Key Vault Private Endpoint." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + } + }, + "resourceGroupResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the Resource Group the Private Endpoint will be created in. If not specified, the Resource Group of the provided Virtual Network Subnet is used." + } + }, + "privateDnsZoneGroup": { + "$ref": "#/definitions/_1.privateEndpointPrivateDnsZoneGroupType", + "nullable": true, + "metadata": { + "description": "Optional. The private DNS Zone Group to configure for the Private Endpoint." + } + }, + "isManualConnection": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. If Manual Private Link Connection is required." + } + }, + "manualConnectionRequestMessage": { + "type": "string", + "nullable": true, + "maxLength": 140, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with the manual connection request." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/_1.privateEndpointCustomDnsConfigType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Custom DNS configurations." + } + }, + "ipConfigurations": { + "type": "array", + "items": { + "$ref": "#/definitions/_1.privateEndpointIpConfigurationType" + }, + "nullable": true, + "metadata": { + "description": "Optional. A list of IP configurations of the Private Endpoint. This will be used to map to the first-party Service endpoints." + } + }, + "applicationSecurityGroupResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Application security groups in which the Private Endpoint IP configuration is included." + } + }, + "customNetworkInterfaceName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The custom name of the network interface attached to the Private Endpoint." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags to be applied on all resources/Resource Groups in this deployment." + } + }, + "enableTelemetry": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a private endpoint. To be used if the private endpoint's default service / groupId can be assumed (i.e., for services that only have one Private Endpoint type like 'vault' for key vault).", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "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.5.1" + } + } + }, + "secretsOutputType": { + "type": "object", + "properties": {}, + "additionalProperties": { + "$ref": "#/definitions/_1.secretSetOutputType", + "metadata": { + "description": "An exported secret's references." + } + }, + "metadata": { + "description": "A map of the exported secrets", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of Cognitive Services account." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "AIServices", + "AnomalyDetector", + "CognitiveServices", + "ComputerVision", + "ContentModerator", + "ContentSafety", + "ConversationalLanguageUnderstanding", + "CustomVision.Prediction", + "CustomVision.Training", + "Face", + "FormRecognizer", + "HealthInsights", + "ImmersiveReader", + "Internal.AllInOne", + "LUIS", + "LUIS.Authoring", + "LanguageAuthoring", + "MetricsAdvisor", + "OpenAI", + "Personalizer", + "QnAMaker.v2", + "SpeechServices", + "TextAnalytics", + "TextTranslation" + ], + "metadata": { + "description": "Required. Kind of the Cognitive Services account. Use 'Get-AzCognitiveServicesAccountSku' to determine a valid combinations of 'kind' and 'SKU' for your Azure region." + } + }, + "sku": { + "type": "string", + "defaultValue": "S0", + "allowedValues": [ + "C2", + "C3", + "C4", + "F0", + "F1", + "S", + "S0", + "S1", + "S10", + "S2", + "S3", + "S4", + "S5", + "S6", + "S7", + "S8", + "S9" + ], + "metadata": { + "description": "Optional. SKU of the Cognitive Services account. Use 'Get-AzCognitiveServicesAccountSku' to determine a valid combinations of 'kind' and 'SKU' for your Azure region." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all Resources." + } + }, + "diagnosticSettings": { + "type": "array", + "items": { + "$ref": "#/definitions/diagnosticSettingFullType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "publicNetworkAccess": { + "type": "string", + "nullable": true, + "allowedValues": [ + "Enabled", + "Disabled" + ], + "metadata": { + "description": "Optional. Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set and networkAcls are not set." + } + }, + "customSubDomainName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. Subdomain name used for token-based authentication. Required if 'networkAcls' or 'privateEndpoints' are set." + } + }, + "networkAcls": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. A collection of rules governing the accessibility from specific network locations." + } + }, + "privateEndpoints": { + "type": "array", + "items": { + "$ref": "#/definitions/privateEndpointSingleServiceType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible." + } + }, + "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", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "allowedFqdnList": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. List of allowed FQDN." + } + }, + "apiProperties": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The API properties for special APIs." + } + }, + "disableLocalAuth": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Allow only Azure AD authentication. Should be enabled for security reasons." + } + }, + "customerManagedKey": { + "$ref": "#/definitions/customerManagedKeyType", + "nullable": true, + "metadata": { + "description": "Optional. The customer managed key definition." + } + }, + "dynamicThrottlingEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. The flag to enable dynamic throttling." + } + }, + "migrationToken": { + "type": "securestring", + "nullable": true, + "metadata": { + "description": "Optional. Resource migration token." + } + }, + "restore": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Restore a soft-deleted cognitive service at deployment time. Will fail if no such soft-deleted resource exists." + } + }, + "restrictOutboundNetworkAccess": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Restrict outbound network access." + } + }, + "userOwnedStorage": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The storage accounts for this resource." + } + }, + "managedIdentities": { + "$ref": "#/definitions/managedIdentityAllType", + "nullable": true, + "metadata": { + "description": "Optional. The managed identity definition for this resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "deployments": { + "type": "array", + "items": { + "$ref": "#/definitions/deploymentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of deployments about cognitive service accounts to create." + } + }, + "secretsExportConfiguration": { + "$ref": "#/definitions/secretsExportConfigurationType", + "nullable": true, + "metadata": { + "description": "Optional. Key vault reference and secret settings for the module's secrets export." + } + }, + "allowProjectManagement": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable/Disable project management feature for AI Foundry." + } + } + }, + "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)))))]" + } + ], + "enableReferencedModulesTelemetry": false, + "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", + "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned, UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', null())), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", + "builtInRoleNames": { + "Cognitive Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '25fbc0a9-bd7c-42a3-aa1a-3b75d497ee68')]", + "Cognitive Services Custom Vision Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c1ff6cc2-c111-46fe-8896-e0ef812ad9f3')]", + "Cognitive Services Custom Vision Deployment": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5c4089e1-6d96-4d2f-b296-c1bc7137275f')]", + "Cognitive Services Custom Vision Labeler": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '88424f51-ebe7-446f-bc41-7fa16989e96c')]", + "Cognitive Services Custom Vision Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '93586559-c37d-4a6b-ba08-b9f0940c2d73')]", + "Cognitive Services Custom Vision Trainer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0a5ae4ab-0d65-4eeb-be61-29fc9b54394b')]", + "Cognitive Services Data Reader (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b59867f0-fa02-499b-be73-45a86b5b3e1c')]", + "Cognitive Services Face Recognizer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9894cab4-e18a-44aa-828b-cb588cd6f2d7')]", + "Cognitive Services Immersive Reader User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b2de6794-95db-4659-8781-7e080d3f2b9d')]", + "Cognitive Services Language Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f07febfe-79bc-46b1-8b37-790e26e6e498')]", + "Cognitive Services Language Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7628b7b8-a8b2-4cdc-b46f-e9b35248918e')]", + "Cognitive Services Language Writer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f2310ca1-dc64-4889-bb49-c8e0fa3d47a8')]", + "Cognitive Services LUIS Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f72c8140-2111-481c-87ff-72b910f6e3f8')]", + "Cognitive Services LUIS Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18e81cdc-4e98-4e29-a639-e7d10c5a6226')]", + "Cognitive Services LUIS Writer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6322a993-d5c9-4bed-b113-e49bbea25b27')]", + "Cognitive Services Metrics Advisor Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'cb43c632-a144-4ec5-977c-e80c4affc34a')]", + "Cognitive Services Metrics Advisor User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3b20f47b-3825-43cb-8114-4bd2201156a8')]", + "Cognitive Services OpenAI Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a001fd3d-188f-4b5d-821b-7da978bf7442')]", + "Cognitive Services OpenAI User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e0bd9bd-7b93-4f28-af87-19fc36ad61bd')]", + "Cognitive Services QnA Maker Editor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f4cc2bf9-21be-47a1-bdf1-5c5804381025')]", + "Cognitive Services QnA Maker Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '466ccd10-b268-4a11-b098-b4849f024126')]", + "Cognitive Services Speech Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0e75ca1e-0464-4b4d-8b93-68208a576181')]", + "Cognitive Services Speech User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f2dc8367-1007-4938-bd23-fe263f013447')]", + "Cognitive Services User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a97b65f3-24c7-4388-baec-2e87135dc908')]", + "Azure AI Developer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '64702f94-c441-49e6-a78b-ef80e0188fee')]", + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "cMKKeyVault::cMKKey": { + "condition": "[and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), not(empty(tryGet(parameters('customerManagedKey'), 'keyName')))))]", + "existing": true, + "type": "Microsoft.KeyVault/vaults/keys", + "apiVersion": "2023-07-01", + "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[2]]", + "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[4]]", + "name": "[format('{0}/{1}', last(split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')), tryGet(parameters('customerManagedKey'), 'keyName'))]" + }, + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.cognitiveservices-account.{0}.{1}', replace('0.11.0', '.', '-'), 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" + } + } + } + } + }, + "cMKKeyVault": { + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId')))]", + "existing": true, + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2023-07-01", + "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[2]]", + "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[4]]", + "name": "[last(split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/'))]" + }, + "cMKUserAssignedIdentity": { + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId')))]", + "existing": true, + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2025-01-31-preview", + "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/')[2]]", + "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/')[4]]", + "name": "[last(split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/'))]" + }, + "cognitiveService": { + "type": "Microsoft.CognitiveServices/accounts", + "apiVersion": "2025-04-01-preview", + "name": "[parameters('name')]", + "kind": "[parameters('kind')]", + "identity": "[variables('identity')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "sku": { + "name": "[parameters('sku')]" + }, + "properties": { + "allowProjectManagement": "[parameters('allowProjectManagement')]", + "customSubDomainName": "[parameters('customSubDomainName')]", + "networkAcls": "[if(not(empty(coalesce(parameters('networkAcls'), createObject()))), createObject('defaultAction', tryGet(parameters('networkAcls'), 'defaultAction'), 'virtualNetworkRules', coalesce(tryGet(parameters('networkAcls'), 'virtualNetworkRules'), createArray()), 'ipRules', coalesce(tryGet(parameters('networkAcls'), 'ipRules'), createArray())), null())]", + "publicNetworkAccess": "[if(not(equals(parameters('publicNetworkAccess'), null())), parameters('publicNetworkAccess'), if(not(empty(parameters('networkAcls'))), 'Enabled', 'Disabled'))]", + "allowedFqdnList": "[parameters('allowedFqdnList')]", + "apiProperties": "[parameters('apiProperties')]", + "disableLocalAuth": "[parameters('disableLocalAuth')]", + "encryption": "[if(not(empty(parameters('customerManagedKey'))), createObject('keySource', 'Microsoft.KeyVault', 'keyVaultProperties', createObject('identityClientId', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), ''))), reference('cMKUserAssignedIdentity').clientId, null()), 'keyVaultUri', reference('cMKKeyVault').vaultUri, 'keyName', parameters('customerManagedKey').keyName, 'keyVersion', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'keyVersion'), ''))), tryGet(parameters('customerManagedKey'), 'keyVersion'), last(split(reference('cMKKeyVault::cMKKey').keyUriWithVersion, '/'))))), null())]", + "migrationToken": "[parameters('migrationToken')]", + "restore": "[parameters('restore')]", + "restrictOutboundNetworkAccess": "[parameters('restrictOutboundNetworkAccess')]", + "userOwnedStorage": "[parameters('userOwnedStorage')]", + "dynamicThrottlingEnabled": "[parameters('dynamicThrottlingEnabled')]" + }, + "dependsOn": [ + "cMKKeyVault", + "cMKKeyVault::cMKKey", + "cMKUserAssignedIdentity" + ] + }, + "cognitiveService_deployments": { + "copy": { + "name": "cognitiveService_deployments", + "count": "[length(coalesce(parameters('deployments'), createArray()))]", + "mode": "serial", + "batchSize": 1 + }, + "type": "Microsoft.CognitiveServices/accounts/deployments", + "apiVersion": "2025-04-01-preview", + "name": "[format('{0}/{1}', parameters('name'), coalesce(tryGet(coalesce(parameters('deployments'), createArray())[copyIndex()], 'name'), format('{0}-deployments', parameters('name'))))]", + "properties": { + "model": "[coalesce(parameters('deployments'), createArray())[copyIndex()].model]", + "raiPolicyName": "[tryGet(coalesce(parameters('deployments'), createArray())[copyIndex()], 'raiPolicyName')]", + "versionUpgradeOption": "[tryGet(coalesce(parameters('deployments'), createArray())[copyIndex()], 'versionUpgradeOption')]" + }, + "sku": "[coalesce(tryGet(coalesce(parameters('deployments'), createArray())[copyIndex()], 'sku'), createObject('name', parameters('sku'), 'capacity', tryGet(parameters('sku'), 'capacity'), 'tier', tryGet(parameters('sku'), 'tier'), 'size', tryGet(parameters('sku'), 'size'), 'family', tryGet(parameters('sku'), 'family')))]", + "dependsOn": [ + "cognitiveService" + ] + }, + "cognitiveService_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.CognitiveServices/accounts/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "cognitiveService" + ] + }, + "cognitiveService_diagnosticSettings": { + "copy": { + "name": "cognitiveService_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.CognitiveServices/accounts/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null + } + }, + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "cognitiveService" + ] + }, + "cognitiveService_roleAssignments": { + "copy": { + "name": "cognitiveService_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.CognitiveServices/accounts/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.CognitiveServices/accounts', 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": [ + "cognitiveService" + ] + }, + "cognitiveService_privateEndpoints": { + "copy": { + "name": "cognitiveService_privateEndpoints", + "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-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]]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'name'), format('pep-{0}-{1}-{2}', last(split(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'account'), copyIndex()))]" + }, + "privateLinkServiceConnections": "[if(not(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true())), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'account'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'account')))))), createObject('value', null()))]", + "manualPrivateLinkServiceConnections": "[if(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true()), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'account'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'account')), 'requestMessage', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualConnectionRequestMessage'), 'Manual approval required.'))))), createObject('value', null()))]", + "subnetResourceId": { + "value": "[coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId]" + }, + "enableTelemetry": { + "value": "[variables('enableReferencedModulesTelemetry')]" + }, + "location": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'location'), reference(split(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location)]" + }, + "lock": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" + }, + "privateDnsZoneGroup": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroup')]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'roleAssignments')]" + }, + "tags": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + }, + "customDnsConfigs": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customDnsConfigs')]" + }, + "ipConfigurations": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'ipConfigurations')]" + }, + "applicationSecurityGroupResourceIds": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'applicationSecurityGroupResourceIds')]" + }, + "customNetworkInterfaceName": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customNetworkInterfaceName')]" + } + }, + "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.34.44.8038", + "templateHash": "12389807800450456797" + }, + "name": "Private Endpoints", + "description": "This module deploys a Private Endpoint." + }, + "definitions": { + "privateDnsZoneGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, + "metadata": { + "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "ipConfigurationType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource that is unique within a resource group." + } + }, + "properties": { + "type": "object", + "properties": { + "groupId": { + "type": "string", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } + } + }, + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "privateLinkServiceConnectionType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the private link service connection." + } + }, + "properties": { + "type": "object", + "properties": { + "groupIds": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." + } + }, + "privateLinkServiceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of private link service." + } + }, + "requestMessage": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." + } + } + }, + "metadata": { + "description": "Required. Properties of private link service connection." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "customDnsConfigType": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. FQDN that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "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." + } + } + }, + "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.5.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": { + "__bicep_imported_from!": { + "sourceTemplate": "private-dns-zone-group/main.bicep" + } + } + }, + "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.5.1" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the private endpoint resource to create." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + } + }, + "applicationSecurityGroupResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Application security groups in which the private endpoint IP configuration is included." + } + }, + "customNetworkInterfaceName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The custom name of the network interface attached to the private endpoint." + } + }, + "ipConfigurations": { + "type": "array", + "items": { + "$ref": "#/definitions/ipConfigurationType" + }, + "nullable": true, + "metadata": { + "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." + } + }, + "privateDnsZoneGroup": { + "$ref": "#/definitions/privateDnsZoneGroupType", + "nullable": true, + "metadata": { + "description": "Optional. The private DNS zone group to configure for the private endpoint." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "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", + "nullable": true, + "metadata": { + "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/customDnsConfigType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Custom DNS configurations." + } + }, + "manualPrivateLinkServiceConnections": { + "type": "array", + "items": { + "$ref": "#/definitions/privateLinkServiceConnectionType" + }, + "nullable": true, + "metadata": { + "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." + } + }, + "privateLinkServiceConnections": { + "type": "array", + "items": { + "$ref": "#/definitions/privateLinkServiceConnectionType" + }, + "nullable": true, + "metadata": { + "description": "Conditional. A grouping of information about the connection to the remote resource. Required if `manualPrivateLinkServiceConnections` is empty." + } + }, + "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": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.11.0', '.', '-'), 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-05-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": "[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": "2022-09-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.34.44.8038", + "templateHash": "13997305779829540948" + }, + "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 + } + } + }, + "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." + } + } + }, + "variables": { + "copy": [ + { + "name": "privateDnsZoneConfigsVar", + "count": "[length(parameters('privateDnsZoneConfigs'))]", + "input": { + "name": "[coalesce(tryGet(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')], 'name'), last(split(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId, '/')))]", + "properties": { + "privateDnsZoneId": "[parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId]" + } + } + } + ] + }, + "resources": { + "privateEndpoint": { + "existing": true, + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2024-05-01", + "name": "[parameters('privateEndpointName')]" + }, + "privateDnsZoneGroup": { + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2024-05-01", + "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", + "properties": { + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" + } + } + }, + "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-05-01', 'full').location]" + }, + "customDnsConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/customDnsConfigType" + }, + "metadata": { + "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": [ + "cognitiveService" + ] + }, + "secretsExport": { + "condition": "[not(equals(parameters('secretsExportConfiguration'), null()))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-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]]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "keyVaultName": { + "value": "[last(split(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '/'))]" + }, + "secretsToSet": { + "value": "[union(createArray(), if(contains(parameters('secretsExportConfiguration'), 'accessKey1Name'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'accessKey1Name'), 'value', listKeys('cognitiveService', '2025-04-01-preview').key1)), createArray()), if(contains(parameters('secretsExportConfiguration'), 'accessKey2Name'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'accessKey2Name'), 'value', listKeys('cognitiveService', '2025-04-01-preview').key2)), createArray()))]" + } + }, + "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.1.42791", + "templateHash": "1200612323329026557" + } + }, + "definitions": { + "secretSetOutputType": { + "type": "object", + "properties": { + "secretResourceId": { + "type": "string", + "metadata": { + "description": "The resourceId of the exported secret." + } + }, + "secretUri": { + "type": "string", + "metadata": { + "description": "The secret URI of the exported secret." + } + }, + "secretUriWithVersion": { + "type": "string", + "metadata": { + "description": "The secret URI with version of the exported secret." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for the output of the secret set via the secrets export feature.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "secretToSetType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the secret to set." + } + }, + "value": { + "type": "securestring", + "metadata": { + "description": "Required. The value of the secret to set." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for the secret to set via the secrets export feature.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + } + }, + "parameters": { + "keyVaultName": { + "type": "string", + "metadata": { + "description": "Required. The name of the Key Vault to set the ecrets in." + } + }, + "secretsToSet": { + "type": "array", + "items": { + "$ref": "#/definitions/secretToSetType" + }, + "metadata": { + "description": "Required. The secrets to set in the Key Vault." + } + } + }, + "resources": { + "keyVault": { + "existing": true, + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2023-07-01", + "name": "[parameters('keyVaultName')]" + }, + "secrets": { + "copy": { + "name": "secrets", + "count": "[length(parameters('secretsToSet'))]" + }, + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2023-07-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('secretsToSet')[copyIndex()].name)]", + "properties": { + "value": "[parameters('secretsToSet')[copyIndex()].value]" + } + } + }, + "outputs": { + "secretsSet": { + "type": "array", + "items": { + "$ref": "#/definitions/secretSetOutputType" + }, + "metadata": { + "description": "The references to the secrets exported to the provided Key Vault." + }, + "copy": { + "count": "[length(range(0, length(coalesce(parameters('secretsToSet'), createArray()))))]", + "input": { + "secretResourceId": "[resourceId('Microsoft.KeyVault/vaults/secrets', parameters('keyVaultName'), parameters('secretsToSet')[range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()]].name)]", + "secretUri": "[reference(format('secrets[{0}]', range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()])).secretUri]", + "secretUriWithVersion": "[reference(format('secrets[{0}]', range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()])).secretUriWithVersion]" + } + } + } + } + } + }, + "dependsOn": [ + "cognitiveService" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the cognitive services account." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the cognitive services account." + }, + "value": "[resourceId('Microsoft.CognitiveServices/accounts', parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the cognitive services account was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "endpoint": { + "type": "string", + "metadata": { + "description": "The service endpoint of the cognitive services account." + }, + "value": "[reference('cognitiveService').endpoint]" + }, + "endpoints": { + "$ref": "#/definitions/endpointType", + "metadata": { + "description": "All endpoints available for the cognitive services account, types depends on the cognitive service kind." + }, + "value": "[reference('cognitiveService').endpoints]" + }, + "systemAssignedMIPrincipalId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "The principal ID of the system assigned identity." + }, + "value": "[tryGet(tryGet(reference('cognitiveService', '2025-04-01-preview', 'full'), 'identity'), 'principalId')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('cognitiveService', '2025-04-01-preview', 'full').location]" + }, + "exportedSecrets": { + "$ref": "#/definitions/secretsOutputType", + "metadata": { + "description": "A hashtable of references to the secrets exported to the provided Key Vault. The key of each reference is each secret's name." + }, + "value": "[if(not(equals(parameters('secretsExportConfiguration'), null())), toObject(reference('secretsExport').outputs.secretsSet.value, lambda('secret', last(split(lambdaVariables('secret').secretResourceId, '/'))), lambda('secret', lambdaVariables('secret'))), createObject())]" + }, + "privateEndpoints": { + "type": "array", + "items": { + "$ref": "#/definitions/privateEndpointOutputType" + }, + "metadata": { + "description": "The private endpoints of the congitive services account." + }, + "copy": { + "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]", + "input": { + "name": "[reference(format('cognitiveService_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", + "resourceId": "[reference(format('cognitiveService_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", + "groupId": "[tryGet(tryGet(reference(format('cognitiveService_privateEndpoints[{0}]', copyIndex())).outputs, 'groupId'), 'value')]", + "customDnsConfigs": "[reference(format('cognitiveService_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfigs.value]", + "networkInterfaceResourceIds": "[reference(format('cognitiveService_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceResourceIds.value]" + } + } + } + } + } + } + } + }, + "outputs": { + "resourceId": { + "type": "string", + "value": "[reference('cognitiveService').outputs.resourceId.value]" + }, + "name": { + "type": "string", + "value": "[reference('cognitiveService').outputs.name.value]" + }, + "systemAssignedMIPrincipalId": { + "type": "string", + "nullable": true, + "value": "[tryGet(tryGet(reference('cognitiveService').outputs, 'systemAssignedMIPrincipalId'), 'value')]" + }, + "endpoint": { + "type": "string", + "value": "[reference('cognitiveService').outputs.endpoint.value]" + }, + "foundryConnection": { + "type": "object", + "value": { + "name": "[reference('cognitiveService').outputs.name.value]", + "value": null, + "category": "[parameters('category')]", + "target": "[reference('cognitiveService').outputs.endpoint.value]", + "kind": "[parameters('kind')]", + "connectionProperties": { + "authType": "AAD" + }, + "isSharedToAll": true, + "metadata": { + "ApiType": "Azure", + "Kind": "[parameters('kind')]", + "ResourceId": "[reference('cognitiveService').outputs.resourceId.value]" + } + } + } + } + } + }, + "dependsOn": [ + "cognitiveServicesPrivateDnsZone" + ] + }, + "translator": { + "condition": "[parameters('translatorEnabled')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('{0}-translator-deployment', parameters('name')), 64)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[format('translator{0}{1}', parameters('name'), parameters('resourceToken'))]" + }, + "location": { + "value": "[parameters('location')]" + }, + "kind": { + "value": "TextTranslation" + }, + "sku": { + "value": "S1" + }, + "networkIsolation": { + "value": "[parameters('networkIsolation')]" + }, + "networkAcls": { + "value": "[parameters('networkAcls')]" + }, + "virtualNetworkSubnetResourceId": "[if(parameters('networkIsolation'), createObject('value', parameters('virtualNetworkSubnetResourceId')), createObject('value', ''))]", + "privateDnsZonesResourceIds": "[if(parameters('networkIsolation'), createObject('value', createArray(reference('cognitiveServicesPrivateDnsZone').outputs.resourceId.value)), createObject('value', createArray()))]", + "logAnalyticsWorkspaceResourceId": { + "value": "[parameters('logAnalyticsWorkspaceResourceId')]" + }, + "roleAssignments": { + "value": "[variables('allRoleAssignments')]" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "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.1.42791", + "templateHash": "2348080591288311162" + } + }, + "definitions": { + "deploymentsType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of cognitive service account deployment." + } + }, + "model": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of Cognitive Services account deployment model." + } + }, + "format": { + "type": "string", + "metadata": { + "description": "Required. The format of Cognitive Services account deployment model." + } + }, + "version": { + "type": "string", + "metadata": { + "description": "Required. The version of Cognitive Services account deployment model." + } + } + }, + "metadata": { + "description": "Required. Properties of Cognitive Services account deployment model." + } + }, + "sku": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource model definition representing SKU." + } + }, + "capacity": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The capacity of the resource model definition representing SKU." + } + }, + "tier": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The tier of the resource model definition representing SKU." + } + }, + "size": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The size of the resource model definition representing SKU." + } + }, + "family": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The family of the resource model definition representing SKU." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The resource model definition representing SKU." + } + }, + "raiPolicyName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of RAI policy." + } + }, + "versionUpgradeOption": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The version upgrade option." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "../customTypes.bicep" + } + } + }, + "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.5.1" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Name of the Cognitive Services resource. Must be unique in the resource group." + } + }, + "location": { + "type": "string", + "metadata": { + "description": "The location of the Cognitive Services resource." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "AIServices", + "AnomalyDetector", + "CognitiveServices", + "ComputerVision", + "ContentModerator", + "ContentSafety", + "ConversationalLanguageUnderstanding", + "CustomVision.Prediction", + "CustomVision.Training", + "Face", + "FormRecognizer", + "HealthInsights", + "ImmersiveReader", + "Internal.AllInOne", + "LUIS", + "LUIS.Authoring", + "LanguageAuthoring", + "MetricsAdvisor", + "OpenAI", + "Personalizer", + "QnAMaker.v2", + "SpeechServices", + "TextAnalytics", + "TextTranslation" + ], + "metadata": { + "description": "Required. Kind of the Cognitive Services account. Use 'Get-AzCognitiveServicesAccountSku' to determine a valid combinations of 'kind' and 'SKU' for your Azure region." + } + }, + "sku": { + "type": "string", + "defaultValue": "S0", + "allowedValues": [ + "S", + "S0", + "S1", + "S2", + "S3", + "S4", + "S5", + "S6", + "S7", + "S8" + ], + "metadata": { + "description": "Required. The SKU of the Cognitive Services account. Use 'Get-AzCognitiveServicesAccountSku' to determine a valid combinations of 'kind' and 'SKU' for your Azure region." + } + }, + "category": { + "type": "string", + "defaultValue": "CognitiveService", + "metadata": { + "description": "Category of the Cognitive Services account." + } + }, + "networkIsolation": { + "type": "bool", + "metadata": { + "description": "Specifies whether to enable network isolation. If true, the resource will be deployed in a private endpoint and public network access will be disabled." + } + }, + "privateDnsZonesResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "defaultValue": [], + "metadata": { + "description": "Existing resource ID of the private DNS zone for the private endpoint." + } + }, + "virtualNetworkSubnetResourceId": { + "type": "string", + "metadata": { + "description": "Resource ID of the subnet for the private endpoint." + } + }, + "logAnalyticsWorkspaceResourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the Log Analytics workspace to use for diagnostic settings." + } + }, + "aiModelDeployments": { + "type": "array", + "items": { + "$ref": "#/definitions/deploymentsType" + }, + "defaultValue": [], + "metadata": { + "description": "Optional. Specifies the OpenAI deployments to create." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Tags to be applied to the resources." + } + }, + "networkAcls": { + "type": "object", + "metadata": { + "description": "Optional. A collection of rules governing the accessibility from specific network locations." + } + } + }, + "variables": { + "copy": [ + { + "name": "privateDnsZones", + "count": "[length(parameters('privateDnsZonesResourceIds'))]", + "input": { + "privateDnsZoneResourceId": "[parameters('privateDnsZonesResourceIds')[copyIndex('privateDnsZones')]]" + } + } + ], + "nameFormatted": "[take(toLower(parameters('name')), 24)]" + }, + "resources": { + "cognitiveService": { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('cog-{0}-{1}-deployment', parameters('kind'), parameters('name')), 64)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[variables('nameFormatted')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "sku": { + "value": "[parameters('sku')]" + }, + "kind": { + "value": "[parameters('kind')]" + }, + "allowProjectManagement": { + "value": true + }, + "managedIdentities": { + "value": { + "systemAssigned": true + } + }, + "deployments": { + "value": "[parameters('aiModelDeployments')]" + }, + "customSubDomainName": { + "value": "[parameters('name')]" + }, + "disableLocalAuth": { + "value": "[parameters('networkIsolation')]" + }, + "publicNetworkAccess": "[if(parameters('networkIsolation'), createObject('value', 'Disabled'), createObject('value', 'Enabled'))]", + "diagnosticSettings": { + "value": [ + { + "workspaceResourceId": "[parameters('logAnalyticsWorkspaceResourceId')]" + } + ] + }, + "roleAssignments": { + "value": "[parameters('roleAssignments')]" + }, + "networkAcls": { + "value": "[parameters('networkAcls')]" + }, + "privateEndpoints": "[if(parameters('networkIsolation'), createObject('value', createArray(createObject('privateDnsZoneGroup', createObject('privateDnsZoneGroupConfigs', variables('privateDnsZones')), 'subnetResourceId', parameters('virtualNetworkSubnetResourceId')))), createObject('value', createArray()))]" + }, + "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.1.42791", + "templateHash": "16135659971302525380" + }, + "name": "Cognitive Services", + "description": "This module deploys a Cognitive Service." + }, + "definitions": { + "privateEndpointOutputType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the private endpoint." + } + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the private endpoint." + } + }, + "groupId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "The group Id for the private endpoint Group." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "FQDN that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "A list of private IP addresses of the private endpoint." + } + } + } + }, + "metadata": { + "description": "The custom DNS configurations of the private endpoint." + } + }, + "networkInterfaceResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "The IDs of the network interfaces associated with the private endpoint." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for the private endpoint output." + } + }, + "deploymentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of cognitive service account deployment." + } + }, + "model": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of Cognitive Services account deployment model." + } + }, + "format": { + "type": "string", + "metadata": { + "description": "Required. The format of Cognitive Services account deployment model." + } + }, + "version": { + "type": "string", + "metadata": { + "description": "Required. The version of Cognitive Services account deployment model." + } + } + }, + "metadata": { + "description": "Required. Properties of Cognitive Services account deployment model." + } + }, + "sku": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource model definition representing SKU." + } + }, + "capacity": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The capacity of the resource model definition representing SKU." + } + }, + "tier": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The tier of the resource model definition representing SKU." + } + }, + "size": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The size of the resource model definition representing SKU." + } + }, + "family": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The family of the resource model definition representing SKU." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The resource model definition representing SKU." + } + }, + "raiPolicyName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of RAI policy." + } + }, + "versionUpgradeOption": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The version upgrade option." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for a cognitive services account deployment." + } + }, + "endpointType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Type of the endpoint." + } + }, + "endpoint": { + "type": "string", + "nullable": true, + "metadata": { + "description": "The endpoint URI." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for a cognitive services account endpoint." + } + }, + "secretsExportConfigurationType": { + "type": "object", + "properties": { + "keyVaultResourceId": { + "type": "string", + "metadata": { + "description": "Required. The key vault name where to store the keys and connection strings generated by the modules." + } + }, + "accessKey1Name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name for the accessKey1 secret to create." + } + }, + "accessKey2Name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name for the accessKey2 secret to create." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type of the secrets exported to the provided Key Vault." + } + }, + "_1.privateEndpointCustomDnsConfigType": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. FQDN that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "_1.privateEndpointIpConfigurationType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource that is unique within a resource group." + } + }, + "properties": { + "type": "object", + "properties": { + "groupId": { + "type": "string", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } + } + }, + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "_1.privateEndpointPrivateDnsZoneGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "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": "Required. The private DNS Zone Groups to associate the Private Endpoint. A DNS Zone Group can support up to 5 DNS zones." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "_1.secretSetOutputType": { + "type": "object", + "properties": { + "secretResourceId": { + "type": "string", + "metadata": { + "description": "The resourceId of the exported secret." + } + }, + "secretUri": { + "type": "string", + "metadata": { + "description": "The secret URI of the exported secret." + } + }, + "secretUriWithVersion": { + "type": "string", + "metadata": { + "description": "The secret URI with version of the exported secret." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for the output of the secret set via the secrets export feature.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "customerManagedKeyType": { + "type": "object", + "properties": { + "keyVaultResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of a key vault to reference a customer managed key for encryption from." + } + }, + "keyName": { + "type": "string", + "metadata": { + "description": "Required. The name of the customer managed key to use for encryption." + } + }, + "keyVersion": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The version of the customer managed key to reference for encryption. If not provided, the deployment will use the latest version available at deployment time." + } + }, + "userAssignedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a customer-managed key. To be used if the resource type does not support auto-rotation of the customer-managed key.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "diagnosticSettingFullType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "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 logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "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 both logs & metrics are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "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." + } + } + }, + "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.5.1" + } + } + }, + "managedIdentityAllType": { + "type": "object", + "properties": { + "systemAssigned": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enables system assigned managed identity on the resource." + } + }, + "userAssignedResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The resource ID(s) to assign to the resource. Required if a user assigned identity is used for encryption." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a managed identity configuration. To be used if both a system-assigned & user-assigned identities are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "privateEndpointSingleServiceType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private Endpoint." + } + }, + "location": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The location to deploy the Private Endpoint to." + } + }, + "privateLinkServiceConnectionName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private link connection to create." + } + }, + "service": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The subresource to deploy the Private Endpoint for. For example \"vault\" for a Key Vault Private Endpoint." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + } + }, + "resourceGroupResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the Resource Group the Private Endpoint will be created in. If not specified, the Resource Group of the provided Virtual Network Subnet is used." + } + }, + "privateDnsZoneGroup": { + "$ref": "#/definitions/_1.privateEndpointPrivateDnsZoneGroupType", + "nullable": true, + "metadata": { + "description": "Optional. The private DNS Zone Group to configure for the Private Endpoint." + } + }, + "isManualConnection": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. If Manual Private Link Connection is required." + } + }, + "manualConnectionRequestMessage": { + "type": "string", + "nullable": true, + "maxLength": 140, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with the manual connection request." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/_1.privateEndpointCustomDnsConfigType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Custom DNS configurations." + } + }, + "ipConfigurations": { + "type": "array", + "items": { + "$ref": "#/definitions/_1.privateEndpointIpConfigurationType" + }, + "nullable": true, + "metadata": { + "description": "Optional. A list of IP configurations of the Private Endpoint. This will be used to map to the first-party Service endpoints." + } + }, + "applicationSecurityGroupResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Application security groups in which the Private Endpoint IP configuration is included." + } + }, + "customNetworkInterfaceName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The custom name of the network interface attached to the Private Endpoint." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags to be applied on all resources/Resource Groups in this deployment." + } + }, + "enableTelemetry": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a private endpoint. To be used if the private endpoint's default service / groupId can be assumed (i.e., for services that only have one Private Endpoint type like 'vault' for key vault).", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "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.5.1" + } + } + }, + "secretsOutputType": { + "type": "object", + "properties": {}, + "additionalProperties": { + "$ref": "#/definitions/_1.secretSetOutputType", + "metadata": { + "description": "An exported secret's references." + } + }, + "metadata": { + "description": "A map of the exported secrets", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of Cognitive Services account." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "AIServices", + "AnomalyDetector", + "CognitiveServices", + "ComputerVision", + "ContentModerator", + "ContentSafety", + "ConversationalLanguageUnderstanding", + "CustomVision.Prediction", + "CustomVision.Training", + "Face", + "FormRecognizer", + "HealthInsights", + "ImmersiveReader", + "Internal.AllInOne", + "LUIS", + "LUIS.Authoring", + "LanguageAuthoring", + "MetricsAdvisor", + "OpenAI", + "Personalizer", + "QnAMaker.v2", + "SpeechServices", + "TextAnalytics", + "TextTranslation" + ], + "metadata": { + "description": "Required. Kind of the Cognitive Services account. Use 'Get-AzCognitiveServicesAccountSku' to determine a valid combinations of 'kind' and 'SKU' for your Azure region." + } + }, + "sku": { + "type": "string", + "defaultValue": "S0", + "allowedValues": [ + "C2", + "C3", + "C4", + "F0", + "F1", + "S", + "S0", + "S1", + "S10", + "S2", + "S3", + "S4", + "S5", + "S6", + "S7", + "S8", + "S9" + ], + "metadata": { + "description": "Optional. SKU of the Cognitive Services account. Use 'Get-AzCognitiveServicesAccountSku' to determine a valid combinations of 'kind' and 'SKU' for your Azure region." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all Resources." + } + }, + "diagnosticSettings": { + "type": "array", + "items": { + "$ref": "#/definitions/diagnosticSettingFullType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "publicNetworkAccess": { + "type": "string", + "nullable": true, + "allowedValues": [ + "Enabled", + "Disabled" + ], + "metadata": { + "description": "Optional. Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set and networkAcls are not set." + } + }, + "customSubDomainName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. Subdomain name used for token-based authentication. Required if 'networkAcls' or 'privateEndpoints' are set." + } + }, + "networkAcls": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. A collection of rules governing the accessibility from specific network locations." + } + }, + "privateEndpoints": { + "type": "array", + "items": { + "$ref": "#/definitions/privateEndpointSingleServiceType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible." + } + }, + "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", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "allowedFqdnList": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. List of allowed FQDN." + } + }, + "apiProperties": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The API properties for special APIs." + } + }, + "disableLocalAuth": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Allow only Azure AD authentication. Should be enabled for security reasons." + } + }, + "customerManagedKey": { + "$ref": "#/definitions/customerManagedKeyType", + "nullable": true, + "metadata": { + "description": "Optional. The customer managed key definition." + } + }, + "dynamicThrottlingEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. The flag to enable dynamic throttling." + } + }, + "migrationToken": { + "type": "securestring", + "nullable": true, + "metadata": { + "description": "Optional. Resource migration token." + } + }, + "restore": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Restore a soft-deleted cognitive service at deployment time. Will fail if no such soft-deleted resource exists." + } + }, + "restrictOutboundNetworkAccess": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Restrict outbound network access." + } + }, + "userOwnedStorage": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The storage accounts for this resource." + } + }, + "managedIdentities": { + "$ref": "#/definitions/managedIdentityAllType", + "nullable": true, + "metadata": { + "description": "Optional. The managed identity definition for this resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "deployments": { + "type": "array", + "items": { + "$ref": "#/definitions/deploymentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of deployments about cognitive service accounts to create." + } + }, + "secretsExportConfiguration": { + "$ref": "#/definitions/secretsExportConfigurationType", + "nullable": true, + "metadata": { + "description": "Optional. Key vault reference and secret settings for the module's secrets export." + } + }, + "allowProjectManagement": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable/Disable project management feature for AI Foundry." + } + } + }, + "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)))))]" + } + ], + "enableReferencedModulesTelemetry": false, + "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", + "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned, UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', null())), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", + "builtInRoleNames": { + "Cognitive Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '25fbc0a9-bd7c-42a3-aa1a-3b75d497ee68')]", + "Cognitive Services Custom Vision Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c1ff6cc2-c111-46fe-8896-e0ef812ad9f3')]", + "Cognitive Services Custom Vision Deployment": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5c4089e1-6d96-4d2f-b296-c1bc7137275f')]", + "Cognitive Services Custom Vision Labeler": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '88424f51-ebe7-446f-bc41-7fa16989e96c')]", + "Cognitive Services Custom Vision Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '93586559-c37d-4a6b-ba08-b9f0940c2d73')]", + "Cognitive Services Custom Vision Trainer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0a5ae4ab-0d65-4eeb-be61-29fc9b54394b')]", + "Cognitive Services Data Reader (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b59867f0-fa02-499b-be73-45a86b5b3e1c')]", + "Cognitive Services Face Recognizer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9894cab4-e18a-44aa-828b-cb588cd6f2d7')]", + "Cognitive Services Immersive Reader User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b2de6794-95db-4659-8781-7e080d3f2b9d')]", + "Cognitive Services Language Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f07febfe-79bc-46b1-8b37-790e26e6e498')]", + "Cognitive Services Language Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7628b7b8-a8b2-4cdc-b46f-e9b35248918e')]", + "Cognitive Services Language Writer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f2310ca1-dc64-4889-bb49-c8e0fa3d47a8')]", + "Cognitive Services LUIS Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f72c8140-2111-481c-87ff-72b910f6e3f8')]", + "Cognitive Services LUIS Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18e81cdc-4e98-4e29-a639-e7d10c5a6226')]", + "Cognitive Services LUIS Writer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6322a993-d5c9-4bed-b113-e49bbea25b27')]", + "Cognitive Services Metrics Advisor Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'cb43c632-a144-4ec5-977c-e80c4affc34a')]", + "Cognitive Services Metrics Advisor User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3b20f47b-3825-43cb-8114-4bd2201156a8')]", + "Cognitive Services OpenAI Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a001fd3d-188f-4b5d-821b-7da978bf7442')]", + "Cognitive Services OpenAI User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e0bd9bd-7b93-4f28-af87-19fc36ad61bd')]", + "Cognitive Services QnA Maker Editor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f4cc2bf9-21be-47a1-bdf1-5c5804381025')]", + "Cognitive Services QnA Maker Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '466ccd10-b268-4a11-b098-b4849f024126')]", + "Cognitive Services Speech Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0e75ca1e-0464-4b4d-8b93-68208a576181')]", + "Cognitive Services Speech User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f2dc8367-1007-4938-bd23-fe263f013447')]", + "Cognitive Services User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a97b65f3-24c7-4388-baec-2e87135dc908')]", + "Azure AI Developer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '64702f94-c441-49e6-a78b-ef80e0188fee')]", + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "cMKKeyVault::cMKKey": { + "condition": "[and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), not(empty(tryGet(parameters('customerManagedKey'), 'keyName')))))]", + "existing": true, + "type": "Microsoft.KeyVault/vaults/keys", + "apiVersion": "2023-07-01", + "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[2]]", + "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[4]]", + "name": "[format('{0}/{1}', last(split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')), tryGet(parameters('customerManagedKey'), 'keyName'))]" + }, + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.cognitiveservices-account.{0}.{1}', replace('0.11.0', '.', '-'), 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" + } + } + } + } + }, + "cMKKeyVault": { + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId')))]", + "existing": true, + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2023-07-01", + "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[2]]", + "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[4]]", + "name": "[last(split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/'))]" + }, + "cMKUserAssignedIdentity": { + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId')))]", + "existing": true, + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2025-01-31-preview", + "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/')[2]]", + "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/')[4]]", + "name": "[last(split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/'))]" + }, + "cognitiveService": { + "type": "Microsoft.CognitiveServices/accounts", + "apiVersion": "2025-04-01-preview", + "name": "[parameters('name')]", + "kind": "[parameters('kind')]", + "identity": "[variables('identity')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "sku": { + "name": "[parameters('sku')]" + }, + "properties": { + "allowProjectManagement": "[parameters('allowProjectManagement')]", + "customSubDomainName": "[parameters('customSubDomainName')]", + "networkAcls": "[if(not(empty(coalesce(parameters('networkAcls'), createObject()))), createObject('defaultAction', tryGet(parameters('networkAcls'), 'defaultAction'), 'virtualNetworkRules', coalesce(tryGet(parameters('networkAcls'), 'virtualNetworkRules'), createArray()), 'ipRules', coalesce(tryGet(parameters('networkAcls'), 'ipRules'), createArray())), null())]", + "publicNetworkAccess": "[if(not(equals(parameters('publicNetworkAccess'), null())), parameters('publicNetworkAccess'), if(not(empty(parameters('networkAcls'))), 'Enabled', 'Disabled'))]", + "allowedFqdnList": "[parameters('allowedFqdnList')]", + "apiProperties": "[parameters('apiProperties')]", + "disableLocalAuth": "[parameters('disableLocalAuth')]", + "encryption": "[if(not(empty(parameters('customerManagedKey'))), createObject('keySource', 'Microsoft.KeyVault', 'keyVaultProperties', createObject('identityClientId', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), ''))), reference('cMKUserAssignedIdentity').clientId, null()), 'keyVaultUri', reference('cMKKeyVault').vaultUri, 'keyName', parameters('customerManagedKey').keyName, 'keyVersion', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'keyVersion'), ''))), tryGet(parameters('customerManagedKey'), 'keyVersion'), last(split(reference('cMKKeyVault::cMKKey').keyUriWithVersion, '/'))))), null())]", + "migrationToken": "[parameters('migrationToken')]", + "restore": "[parameters('restore')]", + "restrictOutboundNetworkAccess": "[parameters('restrictOutboundNetworkAccess')]", + "userOwnedStorage": "[parameters('userOwnedStorage')]", + "dynamicThrottlingEnabled": "[parameters('dynamicThrottlingEnabled')]" + }, + "dependsOn": [ + "cMKKeyVault", + "cMKKeyVault::cMKKey", + "cMKUserAssignedIdentity" + ] + }, + "cognitiveService_deployments": { + "copy": { + "name": "cognitiveService_deployments", + "count": "[length(coalesce(parameters('deployments'), createArray()))]", + "mode": "serial", + "batchSize": 1 + }, + "type": "Microsoft.CognitiveServices/accounts/deployments", + "apiVersion": "2025-04-01-preview", + "name": "[format('{0}/{1}', parameters('name'), coalesce(tryGet(coalesce(parameters('deployments'), createArray())[copyIndex()], 'name'), format('{0}-deployments', parameters('name'))))]", + "properties": { + "model": "[coalesce(parameters('deployments'), createArray())[copyIndex()].model]", + "raiPolicyName": "[tryGet(coalesce(parameters('deployments'), createArray())[copyIndex()], 'raiPolicyName')]", + "versionUpgradeOption": "[tryGet(coalesce(parameters('deployments'), createArray())[copyIndex()], 'versionUpgradeOption')]" + }, + "sku": "[coalesce(tryGet(coalesce(parameters('deployments'), createArray())[copyIndex()], 'sku'), createObject('name', parameters('sku'), 'capacity', tryGet(parameters('sku'), 'capacity'), 'tier', tryGet(parameters('sku'), 'tier'), 'size', tryGet(parameters('sku'), 'size'), 'family', tryGet(parameters('sku'), 'family')))]", + "dependsOn": [ + "cognitiveService" + ] + }, + "cognitiveService_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.CognitiveServices/accounts/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "cognitiveService" + ] + }, + "cognitiveService_diagnosticSettings": { + "copy": { + "name": "cognitiveService_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.CognitiveServices/accounts/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null + } + }, + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "cognitiveService" + ] + }, + "cognitiveService_roleAssignments": { + "copy": { + "name": "cognitiveService_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.CognitiveServices/accounts/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.CognitiveServices/accounts', 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": [ + "cognitiveService" + ] + }, + "cognitiveService_privateEndpoints": { + "copy": { + "name": "cognitiveService_privateEndpoints", + "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-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]]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'name'), format('pep-{0}-{1}-{2}', last(split(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'account'), copyIndex()))]" + }, + "privateLinkServiceConnections": "[if(not(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true())), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'account'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'account')))))), createObject('value', null()))]", + "manualPrivateLinkServiceConnections": "[if(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true()), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'account'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'account')), 'requestMessage', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualConnectionRequestMessage'), 'Manual approval required.'))))), createObject('value', null()))]", + "subnetResourceId": { + "value": "[coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId]" + }, + "enableTelemetry": { + "value": "[variables('enableReferencedModulesTelemetry')]" + }, + "location": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'location'), reference(split(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location)]" + }, + "lock": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" + }, + "privateDnsZoneGroup": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroup')]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'roleAssignments')]" + }, + "tags": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + }, + "customDnsConfigs": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customDnsConfigs')]" + }, + "ipConfigurations": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'ipConfigurations')]" + }, + "applicationSecurityGroupResourceIds": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'applicationSecurityGroupResourceIds')]" + }, + "customNetworkInterfaceName": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customNetworkInterfaceName')]" + } + }, + "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.34.44.8038", + "templateHash": "12389807800450456797" + }, + "name": "Private Endpoints", + "description": "This module deploys a Private Endpoint." + }, + "definitions": { + "privateDnsZoneGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, + "metadata": { + "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "ipConfigurationType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource that is unique within a resource group." + } + }, + "properties": { + "type": "object", + "properties": { + "groupId": { + "type": "string", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } + } + }, + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "privateLinkServiceConnectionType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the private link service connection." + } + }, + "properties": { + "type": "object", + "properties": { + "groupIds": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." + } + }, + "privateLinkServiceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of private link service." + } + }, + "requestMessage": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." + } + } }, "metadata": { "description": "Required. Properties of private link service connection." @@ -50198,11 +58191,11 @@ "cognitiveServicesPrivateDnsZone" ] }, - "translator": { - "condition": "[parameters('translatorEnabled')]", + "documentIntelligence": { + "condition": "[parameters('documentIntelligenceEnabled')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[take(format('{0}-translator-deployment', parameters('name')), 64)]", + "name": "[take(format('{0}-doc-intel-deployment', parameters('name')), 64)]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -50210,28 +58203,28 @@ "mode": "Incremental", "parameters": { "name": { - "value": "[format('translator{0}{1}', parameters('name'), parameters('resourceToken'))]" + "value": "[format('docintel{0}{1}', parameters('name'), parameters('resourceToken'))]" }, "location": { "value": "[parameters('location')]" }, "kind": { - "value": "TextTranslation" - }, - "sku": { - "value": "S1" + "value": "FormRecognizer" }, "networkIsolation": { "value": "[parameters('networkIsolation')]" }, - "networkAcls": { - "value": "[parameters('networkAcls')]" - }, "virtualNetworkSubnetResourceId": "[if(parameters('networkIsolation'), createObject('value', parameters('virtualNetworkSubnetResourceId')), createObject('value', ''))]", "privateDnsZonesResourceIds": "[if(parameters('networkIsolation'), createObject('value', createArray(reference('cognitiveServicesPrivateDnsZone').outputs.resourceId.value)), createObject('value', createArray()))]", "logAnalyticsWorkspaceResourceId": { "value": "[parameters('logAnalyticsWorkspaceResourceId')]" }, + "roleAssignments": { + "value": "[variables('allRoleAssignments')]" + }, + "networkAcls": { + "value": "[parameters('networkAcls')]" + }, "tags": { "value": "[parameters('tags')]" } @@ -52640,733 +60633,1603 @@ "metadata": { "description": "The custom DNS configurations of the private endpoint." }, - "value": "[reference('privateEndpoint').customDnsConfigs]" - }, - "networkInterfaceResourceIds": { + "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": [ + "cognitiveService" + ] + }, + "secretsExport": { + "condition": "[not(equals(parameters('secretsExportConfiguration'), null()))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-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]]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "keyVaultName": { + "value": "[last(split(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '/'))]" + }, + "secretsToSet": { + "value": "[union(createArray(), if(contains(parameters('secretsExportConfiguration'), 'accessKey1Name'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'accessKey1Name'), 'value', listKeys('cognitiveService', '2025-04-01-preview').key1)), createArray()), if(contains(parameters('secretsExportConfiguration'), 'accessKey2Name'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'accessKey2Name'), 'value', listKeys('cognitiveService', '2025-04-01-preview').key2)), createArray()))]" + } + }, + "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.1.42791", + "templateHash": "1200612323329026557" + } + }, + "definitions": { + "secretSetOutputType": { + "type": "object", + "properties": { + "secretResourceId": { + "type": "string", + "metadata": { + "description": "The resourceId of the exported secret." + } + }, + "secretUri": { + "type": "string", + "metadata": { + "description": "The secret URI of the exported secret." + } + }, + "secretUriWithVersion": { + "type": "string", + "metadata": { + "description": "The secret URI with version of the exported secret." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for the output of the secret set via the secrets export feature.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "secretToSetType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the secret to set." + } + }, + "value": { + "type": "securestring", + "metadata": { + "description": "Required. The value of the secret to set." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for the secret to set via the secrets export feature.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + } + }, + "parameters": { + "keyVaultName": { + "type": "string", + "metadata": { + "description": "Required. The name of the Key Vault to set the ecrets in." + } + }, + "secretsToSet": { + "type": "array", + "items": { + "$ref": "#/definitions/secretToSetType" + }, + "metadata": { + "description": "Required. The secrets to set in the Key Vault." + } + } + }, + "resources": { + "keyVault": { + "existing": true, + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2023-07-01", + "name": "[parameters('keyVaultName')]" + }, + "secrets": { + "copy": { + "name": "secrets", + "count": "[length(parameters('secretsToSet'))]" + }, + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2023-07-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('secretsToSet')[copyIndex()].name)]", + "properties": { + "value": "[parameters('secretsToSet')[copyIndex()].value]" + } + } + }, + "outputs": { + "secretsSet": { "type": "array", "items": { - "type": "string" - }, - "metadata": { - "description": "The resource IDs of the network interfaces associated with the private endpoint." + "$ref": "#/definitions/secretSetOutputType" }, - "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." + "description": "The references to the secrets exported to the provided Key Vault." }, - "value": "[coalesce(tryGet(tryGet(tryGet(tryGet(reference('privateEndpoint'), 'manualPrivateLinkServiceConnections'), 0, 'properties'), 'groupIds'), 0), tryGet(tryGet(tryGet(tryGet(reference('privateEndpoint'), 'privateLinkServiceConnections'), 0, 'properties'), 'groupIds'), 0))]" + "copy": { + "count": "[length(range(0, length(coalesce(parameters('secretsToSet'), createArray()))))]", + "input": { + "secretResourceId": "[resourceId('Microsoft.KeyVault/vaults/secrets', parameters('keyVaultName'), parameters('secretsToSet')[range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()]].name)]", + "secretUri": "[reference(format('secrets[{0}]', range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()])).secretUri]", + "secretUriWithVersion": "[reference(format('secrets[{0}]', range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()])).secretUriWithVersion]" + } + } } } } - }, - "dependsOn": [ - "cognitiveService" - ] + }, + "dependsOn": [ + "cognitiveService" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the cognitive services account." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the cognitive services account." + }, + "value": "[resourceId('Microsoft.CognitiveServices/accounts', parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the cognitive services account was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "endpoint": { + "type": "string", + "metadata": { + "description": "The service endpoint of the cognitive services account." + }, + "value": "[reference('cognitiveService').endpoint]" + }, + "endpoints": { + "$ref": "#/definitions/endpointType", + "metadata": { + "description": "All endpoints available for the cognitive services account, types depends on the cognitive service kind." + }, + "value": "[reference('cognitiveService').endpoints]" + }, + "systemAssignedMIPrincipalId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "The principal ID of the system assigned identity." + }, + "value": "[tryGet(tryGet(reference('cognitiveService', '2025-04-01-preview', 'full'), 'identity'), 'principalId')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('cognitiveService', '2025-04-01-preview', 'full').location]" + }, + "exportedSecrets": { + "$ref": "#/definitions/secretsOutputType", + "metadata": { + "description": "A hashtable of references to the secrets exported to the provided Key Vault. The key of each reference is each secret's name." + }, + "value": "[if(not(equals(parameters('secretsExportConfiguration'), null())), toObject(reference('secretsExport').outputs.secretsSet.value, lambda('secret', last(split(lambdaVariables('secret').secretResourceId, '/'))), lambda('secret', lambdaVariables('secret'))), createObject())]" + }, + "privateEndpoints": { + "type": "array", + "items": { + "$ref": "#/definitions/privateEndpointOutputType" + }, + "metadata": { + "description": "The private endpoints of the congitive services account." + }, + "copy": { + "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]", + "input": { + "name": "[reference(format('cognitiveService_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", + "resourceId": "[reference(format('cognitiveService_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", + "groupId": "[tryGet(tryGet(reference(format('cognitiveService_privateEndpoints[{0}]', copyIndex())).outputs, 'groupId'), 'value')]", + "customDnsConfigs": "[reference(format('cognitiveService_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfigs.value]", + "networkInterfaceResourceIds": "[reference(format('cognitiveService_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceResourceIds.value]" + } + } + } + } + } + } + } + }, + "outputs": { + "resourceId": { + "type": "string", + "value": "[reference('cognitiveService').outputs.resourceId.value]" + }, + "name": { + "type": "string", + "value": "[reference('cognitiveService').outputs.name.value]" + }, + "systemAssignedMIPrincipalId": { + "type": "string", + "nullable": true, + "value": "[tryGet(tryGet(reference('cognitiveService').outputs, 'systemAssignedMIPrincipalId'), 'value')]" + }, + "endpoint": { + "type": "string", + "value": "[reference('cognitiveService').outputs.endpoint.value]" + }, + "foundryConnection": { + "type": "object", + "value": { + "name": "[reference('cognitiveService').outputs.name.value]", + "value": null, + "category": "[parameters('category')]", + "target": "[reference('cognitiveService').outputs.endpoint.value]", + "kind": "[parameters('kind')]", + "connectionProperties": { + "authType": "AAD" + }, + "isSharedToAll": true, + "metadata": { + "ApiType": "Azure", + "Kind": "[parameters('kind')]", + "ResourceId": "[reference('cognitiveService').outputs.resourceId.value]" + } + } + } + } + } + }, + "dependsOn": [ + "cognitiveServicesPrivateDnsZone" + ] + } + }, + "outputs": { + "aiServicesResourceId": { + "type": "string", + "value": "[reference('aiServices').outputs.resourceId.value]" + }, + "aiServicesName": { + "type": "string", + "value": "[reference('aiServices').outputs.name.value]" + }, + "aiServicesEndpoint": { + "type": "string", + "value": "[reference('aiServices').outputs.endpoint.value]" + }, + "aiServicesSystemAssignedMIPrincipalId": { + "type": "string", + "value": "[coalesce(tryGet(tryGet(reference('aiServices').outputs, 'systemAssignedMIPrincipalId'), 'value'), '')]" + }, + "connections": { + "type": "array", + "value": "[union(createArray(reference('aiServices').outputs.foundryConnection.value), if(parameters('contentSafetyEnabled'), createArray(reference('contentSafety').outputs.foundryConnection.value), createArray()), if(parameters('visionEnabled'), createArray(reference('vision').outputs.foundryConnection.value), createArray()), if(parameters('languageEnabled'), createArray(reference('language').outputs.foundryConnection.value), createArray()), if(parameters('speechEnabled'), createArray(reference('speech').outputs.foundryConnection.value), createArray()), if(parameters('translatorEnabled'), createArray(reference('translator').outputs.foundryConnection.value), createArray()), if(parameters('documentIntelligenceEnabled'), createArray(reference('documentIntelligence').outputs.foundryConnection.value), createArray()))]" + } + } + } + }, + "dependsOn": [ + "appIdentity", + "logAnalyticsWorkspace", + "network" + ] + }, + "project": { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}prj', parameters('name'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "cosmosDBname": "[if(parameters('cosmosDbEnabled'), createObject('value', reference('cosmosDb').outputs.cosmosDBname.value), createObject('value', ''))]", + "cosmosDbEnabled": { + "value": "[parameters('cosmosDbEnabled')]" + }, + "searchEnabled": { + "value": "[parameters('searchEnabled')]" + }, + "name": { + "value": "[parameters('projectName')]" + }, + "location": { + "value": "[parameters('aiDeploymentsLocation')]" + }, + "storageName": { + "value": "[reference('storageAccount').outputs.storageName.value]" + }, + "aiServicesName": { + "value": "[reference('cognitiveServices').outputs.aiServicesName.value]" + }, + "nameFormatted": "[if(parameters('searchEnabled'), createObject('value', reference('aiSearch').outputs.name.value), createObject('value', ''))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.36.1.42791", + "templateHash": "15578119539506362308" + } + }, + "parameters": { + "name": { + "type": "string", + "minLength": 3, + "maxLength": 12, + "metadata": { + "description": "The name of the environment. Use alphanumeric characters only." + } + }, + "location": { + "type": "string", + "metadata": { + "description": "Specifies the location for all the Azure resources. Defaults to the location of the resource group." + } + }, + "cosmosDBname": { + "type": "string", + "metadata": { + "description": "Name of the customers existing CosmosDB Resource" + } + }, + "cosmosDbEnabled": { + "type": "bool", + "metadata": { + "description": "Whether to include Cosmos DB in the deployment." + } + }, + "storageName": { + "type": "string", + "metadata": { + "description": "Name of the customers existing Azure Storage Account" + } + }, + "aiServicesName": { + "type": "string", + "metadata": { + "description": "Foundry Account Name" + } + }, + "searchEnabled": { + "type": "bool", + "metadata": { + "description": "Whether to include Azure AI Search in the deployment." + } + }, + "nameFormatted": { + "type": "string", + "metadata": { + "description": "Azure Search Service Name" + } + }, + "defaultProjectName": { + "type": "string", + "defaultValue": "[parameters('name')]", + "metadata": { + "description": "Name of the first project" + } + }, + "defaultProjectDisplayName": { + "type": "string", + "defaultValue": "[parameters('name')]" + }, + "defaultProjectDescription": { + "type": "string", + "defaultValue": "This is a sample project for AI Foundry." + } + }, + "resources": [ + { + "type": "Microsoft.CognitiveServices/accounts/projects", + "apiVersion": "2025-04-01-preview", + "name": "[format('{0}/{1}', parameters('aiServicesName'), parameters('defaultProjectName'))]", + "location": "[parameters('location')]", + "identity": { + "type": "SystemAssigned" + }, + "properties": { + "displayName": "[parameters('defaultProjectDisplayName')]", + "description": "[parameters('defaultProjectDescription')]" + } + }, + { + "type": "Microsoft.CognitiveServices/accounts/projects/connections", + "apiVersion": "2025-04-01-preview", + "name": "[format('{0}/{1}/{2}', parameters('aiServicesName'), parameters('defaultProjectName'), parameters('storageName'))]", + "properties": { + "category": "AzureBlob", + "target": "[reference(resourceId('Microsoft.Storage/storageAccounts', parameters('storageName')), '2023-01-01').primaryEndpoints.blob]", + "authType": "AAD", + "metadata": { + "ApiType": "Azure", + "ResourceId": "[resourceId('Microsoft.Storage/storageAccounts', parameters('storageName'))]", + "location": "[reference(resourceId('Microsoft.Storage/storageAccounts', parameters('storageName')), '2023-01-01', 'full').location]", + "accountName": "[parameters('storageName')]", + "containerName": "[format('{0}proj', parameters('name'))]" + } + }, + "dependsOn": [ + "[resourceId('Microsoft.CognitiveServices/accounts/projects', parameters('aiServicesName'), parameters('defaultProjectName'))]" + ] + }, + { + "condition": "[parameters('searchEnabled')]", + "type": "Microsoft.CognitiveServices/accounts/projects/connections", + "apiVersion": "2025-04-01-preview", + "name": "[format('{0}/{1}/{2}', parameters('aiServicesName'), parameters('defaultProjectName'), if(parameters('searchEnabled'), parameters('nameFormatted'), ''))]", + "properties": { + "category": "CognitiveSearch", + "target": "[if(parameters('searchEnabled'), format('https://{0}.search.windows.net/', parameters('nameFormatted')), '')]", + "authType": "AAD", + "isSharedToAll": true, + "metadata": { + "ApiType": "Azure", + "ResourceId": "[if(parameters('searchEnabled'), resourceId('Microsoft.Search/searchServices', parameters('nameFormatted')), '')]", + "location": "[if(parameters('searchEnabled'), reference(resourceId('Microsoft.Search/searchServices', parameters('nameFormatted')), '2024-06-01-preview', 'full').location, '')]" + } + }, + "dependsOn": [ + "[resourceId('Microsoft.CognitiveServices/accounts/projects', parameters('aiServicesName'), parameters('defaultProjectName'))]" + ] + }, + { + "condition": "[parameters('cosmosDbEnabled')]", + "type": "Microsoft.CognitiveServices/accounts/projects/connections", + "apiVersion": "2025-04-01-preview", + "name": "[format('{0}/{1}/{2}', parameters('aiServicesName'), parameters('defaultProjectName'), parameters('cosmosDBname'))]", + "properties": { + "category": "CosmosDB", + "target": "[if(parameters('cosmosDbEnabled'), reference(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('cosmosDBname')), '2025-05-01-preview').documentEndpoint, '')]", + "authType": "AAD", + "metadata": { + "ApiType": "Azure", + "ResourceId": "[if(parameters('cosmosDbEnabled'), resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('cosmosDBname')), '')]", + "location": "[if(parameters('cosmosDbEnabled'), reference(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('cosmosDBname')), '2025-05-01-preview', 'full').location, '')]" + } + }, + "dependsOn": [ + "[resourceId('Microsoft.CognitiveServices/accounts/projects', parameters('aiServicesName'), parameters('defaultProjectName'))]" + ] + } + ], + "outputs": { + "projectId": { + "type": "string", + "value": "[resourceId('Microsoft.CognitiveServices/accounts/projects', parameters('aiServicesName'), parameters('defaultProjectName'))]" + }, + "projectName": { + "type": "string", + "value": "[parameters('defaultProjectName')]" + } + } + } + }, + "dependsOn": [ + "aiSearch", + "cognitiveServices", + "cosmosDb", + "storageAccount" + ] + }, + "aiSearch": { + "condition": "[parameters('searchEnabled')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('{0}-ai-search-deployment', parameters('name')), 64)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[format('srch{0}{1}', parameters('name'), variables('resourceToken'))]" + }, + "location": { + "value": "[parameters('location')]" + }, + "networkIsolation": { + "value": "[parameters('networkIsolation')]" + }, + "virtualNetworkResourceId": "[if(parameters('networkIsolation'), createObject('value', reference('network').outputs.resourceId.value), createObject('value', ''))]", + "virtualNetworkSubnetResourceId": "[if(parameters('networkIsolation'), createObject('value', reference('network').outputs.defaultSubnetResourceId.value), createObject('value', ''))]", + "logAnalyticsWorkspaceResourceId": { + "value": "[reference('logAnalyticsWorkspace').outputs.resourceId.value]" + }, + "roleAssignments": { + "value": "[union(if(empty(parameters('userObjectId')), createArray(), createArray(createObject('principalId', parameters('userObjectId'), 'principalType', 'User', 'roleDefinitionIdOrName', 'Search Index Data Contributor'), createObject('principalId', parameters('userObjectId'), 'principalType', 'User', 'roleDefinitionIdOrName', 'Search Index Data Reader'))), createArray(createObject('principalId', reference('cognitiveServices').outputs.aiServicesSystemAssignedMIPrincipalId.value, 'principalType', 'ServicePrincipal', 'roleDefinitionIdOrName', 'Search Index Data Contributor'), createObject('principalId', reference('cognitiveServices').outputs.aiServicesSystemAssignedMIPrincipalId.value, 'principalType', 'ServicePrincipal', 'roleDefinitionIdOrName', 'Search Service Contributor')))]" + }, + "tags": { + "value": "[variables('allTags')]" + } + }, + "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.1.42791", + "templateHash": "12098504235093504002" + } + }, + "definitions": { + "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.5.1" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Name of the AI Search resource." + } + }, + "location": { + "type": "string", + "metadata": { + "description": "Specifies the location for all the Azure resources." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Tags to be applied to the resources." + } + }, + "virtualNetworkResourceId": { + "type": "string", + "metadata": { + "description": "Resource ID of the virtual network to link the private DNS zones." + } + }, + "virtualNetworkSubnetResourceId": { + "type": "string", + "metadata": { + "description": "Resource ID of the subnet for the private endpoint." + } + }, + "logAnalyticsWorkspaceResourceId": { + "type": "string", + "metadata": { + "description": "Resource ID of the Log Analytics workspace to use for diagnostic settings." + } + }, + "networkIsolation": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Specifies whether network isolation is enabled. This will create a private endpoint for the AI Search resource and link the private DNS zone." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "nameFormatted": "[take(toLower(parameters('name')), 60)]" + }, + "resources": { + "privateDnsZone": { + "condition": "[parameters('networkIsolation')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "private-dns-search-deployment", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "privatelink.search.windows.net" + }, + "virtualNetworkLinks": { + "value": [ + { + "virtualNetworkResourceId": "[parameters('virtualNetworkResourceId')]" + } + ] + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "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.32.4.45862", + "templateHash": "83178825086050429" + }, + "name": "Private DNS Zones", + "description": "This module deploys a Private DNS zone.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "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." + } + } + } + }, + "nullable": true + }, + "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." + } + } + }, + "nullable": true + }, + "aType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "aRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "ipv4Address": { + "type": "string", + "metadata": { + "description": "Required. The IPv4 address of this A record." + } + } + } }, - "secretsExport": { - "condition": "[not(equals(parameters('secretsExportConfiguration'), null()))]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-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]]", + "nullable": true, + "metadata": { + "description": "Optional. The list of A records in the record set." + } + } + } + }, + "nullable": true + }, + "aaaaType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "aaaaRecords": { + "type": "array", + "items": { + "type": "object", "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "keyVaultName": { - "value": "[last(split(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '/'))]" - }, - "secretsToSet": { - "value": "[union(createArray(), if(contains(parameters('secretsExportConfiguration'), 'accessKey1Name'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'accessKey1Name'), 'value', listKeys('cognitiveService', '2025-04-01-preview').key1)), createArray()), if(contains(parameters('secretsExportConfiguration'), 'accessKey2Name'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'accessKey2Name'), 'value', listKeys('cognitiveService', '2025-04-01-preview').key2)), createArray()))]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "languageVersion": "2.0", - "contentVersion": "1.0.0.0", + "ipv6Address": { + "type": "string", "metadata": { - "_generator": { - "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "1200612323329026557" - } - }, - "definitions": { - "secretSetOutputType": { - "type": "object", - "properties": { - "secretResourceId": { - "type": "string", - "metadata": { - "description": "The resourceId of the exported secret." - } - }, - "secretUri": { - "type": "string", - "metadata": { - "description": "The secret URI of the exported secret." - } - }, - "secretUriWithVersion": { - "type": "string", - "metadata": { - "description": "The secret URI with version of the exported secret." - } - } - }, - "metadata": { - "description": "An AVM-aligned type for the output of the secret set via the secrets export feature.", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } - } - }, - "secretToSetType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the secret to set." - } - }, - "value": { - "type": "securestring", - "metadata": { - "description": "Required. The value of the secret to set." - } - } - }, - "metadata": { - "description": "An AVM-aligned type for the secret to set via the secrets export feature.", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } - } - } - }, - "parameters": { - "keyVaultName": { - "type": "string", - "metadata": { - "description": "Required. The name of the Key Vault to set the ecrets in." - } - }, - "secretsToSet": { - "type": "array", - "items": { - "$ref": "#/definitions/secretToSetType" - }, - "metadata": { - "description": "Required. The secrets to set in the Key Vault." - } - } - }, - "resources": { - "keyVault": { - "existing": true, - "type": "Microsoft.KeyVault/vaults", - "apiVersion": "2023-07-01", - "name": "[parameters('keyVaultName')]" - }, - "secrets": { - "copy": { - "name": "secrets", - "count": "[length(parameters('secretsToSet'))]" - }, - "type": "Microsoft.KeyVault/vaults/secrets", - "apiVersion": "2023-07-01", - "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('secretsToSet')[copyIndex()].name)]", - "properties": { - "value": "[parameters('secretsToSet')[copyIndex()].value]" - } - } - }, - "outputs": { - "secretsSet": { - "type": "array", - "items": { - "$ref": "#/definitions/secretSetOutputType" - }, - "metadata": { - "description": "The references to the secrets exported to the provided Key Vault." - }, - "copy": { - "count": "[length(range(0, length(coalesce(parameters('secretsToSet'), createArray()))))]", - "input": { - "secretResourceId": "[resourceId('Microsoft.KeyVault/vaults/secrets', parameters('keyVaultName'), parameters('secretsToSet')[range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()]].name)]", - "secretUri": "[reference(format('secrets[{0}]', range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()])).secretUri]", - "secretUriWithVersion": "[reference(format('secrets[{0}]', range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()])).secretUriWithVersion]" - } - } - } + "description": "Required. The IPv6 address of this AAAA record." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of AAAA records in the record set." + } + } + } + }, + "nullable": true + }, + "cnameType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "cnameRecord": { + "type": "object", + "properties": { + "cname": { + "type": "string", + "metadata": { + "description": "Required. The canonical name of the CNAME record." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The CNAME record in the record set." + } + } + } + }, + "nullable": true + }, + "mxType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "mxRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "exchange": { + "type": "string", + "metadata": { + "description": "Required. The domain name of the mail host for this MX record." + } + }, + "preference": { + "type": "int", + "metadata": { + "description": "Required. The preference value for this MX record." } } - }, - "dependsOn": [ - "cognitiveService" - ] + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of MX records in the record set." + } + } + } + }, + "nullable": true + }, + "ptrType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." } }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the cognitive services account." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the cognitive services account." - }, - "value": "[resourceId('Microsoft.CognitiveServices/accounts', parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group the cognitive services account was deployed into." - }, - "value": "[resourceGroup().name]" - }, - "endpoint": { - "type": "string", - "metadata": { - "description": "The service endpoint of the cognitive services account." - }, - "value": "[reference('cognitiveService').endpoint]" + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "ptrRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "ptrdname": { + "type": "string", + "metadata": { + "description": "Required. The PTR target domain name for this PTR record." + } + } + } }, - "endpoints": { - "$ref": "#/definitions/endpointType", - "metadata": { - "description": "All endpoints available for the cognitive services account, types depends on the cognitive service kind." + "nullable": true, + "metadata": { + "description": "Optional. The list of PTR records in the record set." + } + } + } + }, + "nullable": true + }, + "soaType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "soaRecord": { + "type": "object", + "properties": { + "email": { + "type": "string", + "metadata": { + "description": "Required. The email contact for this SOA record." + } }, - "value": "[reference('cognitiveService').endpoints]" - }, - "systemAssignedMIPrincipalId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "The principal ID of the system assigned identity." + "expireTime": { + "type": "int", + "metadata": { + "description": "Required. The expire time for this SOA record." + } }, - "value": "[tryGet(tryGet(reference('cognitiveService', '2025-04-01-preview', 'full'), 'identity'), 'principalId')]" - }, - "location": { - "type": "string", - "metadata": { - "description": "The location the resource was deployed into." + "host": { + "type": "string", + "metadata": { + "description": "Required. The domain name of the authoritative name server for this SOA record." + } }, - "value": "[reference('cognitiveService', '2025-04-01-preview', 'full').location]" - }, - "exportedSecrets": { - "$ref": "#/definitions/secretsOutputType", - "metadata": { - "description": "A hashtable of references to the secrets exported to the provided Key Vault. The key of each reference is each secret's name." + "minimumTtl": { + "type": "int", + "metadata": { + "description": "Required. The minimum value for this SOA record. By convention this is used to determine the negative caching duration." + } }, - "value": "[if(not(equals(parameters('secretsExportConfiguration'), null())), toObject(reference('secretsExport').outputs.secretsSet.value, lambda('secret', last(split(lambdaVariables('secret').secretResourceId, '/'))), lambda('secret', lambdaVariables('secret'))), createObject())]" - }, - "privateEndpoints": { - "type": "array", - "items": { - "$ref": "#/definitions/privateEndpointOutputType" + "refreshTime": { + "type": "int", + "metadata": { + "description": "Required. The refresh value for this SOA record." + } }, - "metadata": { - "description": "The private endpoints of the congitive services account." + "retryTime": { + "type": "int", + "metadata": { + "description": "Required. The retry time for this SOA record." + } }, - "copy": { - "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]", - "input": { - "name": "[reference(format('cognitiveService_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", - "resourceId": "[reference(format('cognitiveService_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", - "groupId": "[tryGet(tryGet(reference(format('cognitiveService_privateEndpoints[{0}]', copyIndex())).outputs, 'groupId'), 'value')]", - "customDnsConfigs": "[reference(format('cognitiveService_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfigs.value]", - "networkInterfaceResourceIds": "[reference(format('cognitiveService_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceResourceIds.value]" + "serialNumber": { + "type": "int", + "metadata": { + "description": "Required. The serial number for this SOA record." } } + }, + "nullable": true, + "metadata": { + "description": "Optional. The SOA record in the record set." } } } - } - } - }, - "outputs": { - "resourceId": { - "type": "string", - "value": "[reference('cognitiveService').outputs.resourceId.value]" - }, - "name": { - "type": "string", - "value": "[reference('cognitiveService').outputs.name.value]" - }, - "systemAssignedMIPrincipalId": { - "type": "string", - "nullable": true, - "value": "[tryGet(tryGet(reference('cognitiveService').outputs, 'systemAssignedMIPrincipalId'), 'value')]" - }, - "endpoint": { - "type": "string", - "value": "[reference('cognitiveService').outputs.endpoint.value]" + }, + "nullable": true }, - "foundryConnection": { - "type": "object", - "value": { - "name": "[reference('cognitiveService').outputs.name.value]", - "value": null, - "category": "[parameters('category')]", - "target": "[reference('cognitiveService').outputs.endpoint.value]", - "kind": "[parameters('kind')]", - "connectionProperties": { - "authType": "AAD" - }, - "isSharedToAll": true, - "metadata": { - "ApiType": "Azure", - "Kind": "[parameters('kind')]", - "ResourceId": "[reference('cognitiveService').outputs.resourceId.value]" - } - } - } - } - } - }, - "dependsOn": [ - "cognitiveServicesPrivateDnsZone" - ] - }, - "documentIntelligence": { - "condition": "[parameters('documentIntelligenceEnabled')]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[take(format('{0}-doc-intel-deployment', parameters('name')), 64)]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[format('docintel{0}{1}', parameters('name'), parameters('resourceToken'))]" - }, - "location": { - "value": "[parameters('location')]" - }, - "kind": { - "value": "FormRecognizer" - }, - "networkIsolation": { - "value": "[parameters('networkIsolation')]" - }, - "virtualNetworkSubnetResourceId": "[if(parameters('networkIsolation'), createObject('value', parameters('virtualNetworkSubnetResourceId')), createObject('value', ''))]", - "privateDnsZonesResourceIds": "[if(parameters('networkIsolation'), createObject('value', createArray(reference('cognitiveServicesPrivateDnsZone').outputs.resourceId.value)), createObject('value', createArray()))]", - "logAnalyticsWorkspaceResourceId": { - "value": "[parameters('logAnalyticsWorkspaceResourceId')]" - }, - "networkAcls": { - "value": "[parameters('networkAcls')]" - }, - "tags": { - "value": "[parameters('tags')]" - } - }, - "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.1.42791", - "templateHash": "2348080591288311162" - } - }, - "definitions": { - "deploymentsType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Specify the name of cognitive service account deployment." - } - }, - "model": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of Cognitive Services account deployment model." - } - }, - "format": { - "type": "string", - "metadata": { - "description": "Required. The format of Cognitive Services account deployment model." - } - }, - "version": { - "type": "string", - "metadata": { - "description": "Required. The version of Cognitive Services account deployment model." - } + "srvType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." } }, "metadata": { - "description": "Required. Properties of Cognitive Services account deployment model." - } - }, - "sku": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the resource model definition representing SKU." - } - }, - "capacity": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The capacity of the resource model definition representing SKU." - } - }, - "tier": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The tier of the resource model definition representing SKU." - } - }, - "size": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The size of the resource model definition representing SKU." + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "srvRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "priority": { + "type": "int", + "metadata": { + "description": "Required. The priority value for this SRV record." + } + }, + "weight": { + "type": "int", + "metadata": { + "description": "Required. The weight value for this SRV record." + } + }, + "port": { + "type": "int", + "metadata": { + "description": "Required. The port value for this SRV record." + } + }, + "target": { + "type": "string", + "metadata": { + "description": "Required. The target domain name for this SRV record." + } + } } }, - "family": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The family of the resource model definition representing SKU." - } + "nullable": true, + "metadata": { + "description": "Optional. The list of SRV records in the record set." } - }, - "nullable": true, - "metadata": { - "description": "Optional. The resource model definition representing SKU." - } - }, - "raiPolicyName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of RAI policy." } - }, - "versionUpgradeOption": { - "type": "string", - "nullable": true, + } + }, + "nullable": true + }, + "txtType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, "metadata": { - "description": "Optional. The version upgrade option." + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "txtRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "value": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The text value of this TXT record." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of TXT records in the record set." + } } } }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "../customTypes.bicep" - } - } + "nullable": true }, - "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." + "virtualNetworkLinkType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "minLength": 1, + "maxLength": 80, + "metadata": { + "description": "Optional. The resource name." + } + }, + "virtualNetworkResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the virtual network to link." + } + }, + "location": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Azure Region where the resource lives." + } + }, + "registrationEnabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Is auto-registration of virtual machine records in the virtual network in the Private DNS zone enabled?." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Resource tags." + } + }, + "resolutionPolicy": { + "type": "string", + "allowedValues": [ + "Default", + "NxDomainRedirect" + ], + "nullable": true, + "metadata": { + "description": "Optional. The resolution type of the private-dns-zone fallback machanism." + } } } }, - "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.5.1" - } - } + "nullable": true } }, "parameters": { "name": { "type": "string", "metadata": { - "description": "Name of the Cognitive Services resource. Must be unique in the resource group." + "description": "Required. Private DNS zone name." } }, - "location": { - "type": "string", + "a": { + "$ref": "#/definitions/aType", "metadata": { - "description": "The location of the Cognitive Services resource." + "description": "Optional. Array of A records." } }, - "kind": { - "type": "string", - "allowedValues": [ - "AIServices", - "AnomalyDetector", - "CognitiveServices", - "ComputerVision", - "ContentModerator", - "ContentSafety", - "ConversationalLanguageUnderstanding", - "CustomVision.Prediction", - "CustomVision.Training", - "Face", - "FormRecognizer", - "HealthInsights", - "ImmersiveReader", - "Internal.AllInOne", - "LUIS", - "LUIS.Authoring", - "LanguageAuthoring", - "MetricsAdvisor", - "OpenAI", - "Personalizer", - "QnAMaker.v2", - "SpeechServices", - "TextAnalytics", - "TextTranslation" - ], + "aaaa": { + "$ref": "#/definitions/aaaaType", "metadata": { - "description": "Required. Kind of the Cognitive Services account. Use 'Get-AzCognitiveServicesAccountSku' to determine a valid combinations of 'kind' and 'SKU' for your Azure region." + "description": "Optional. Array of AAAA records." } }, - "sku": { - "type": "string", - "defaultValue": "S0", - "allowedValues": [ - "S", - "S0", - "S1", - "S2", - "S3", - "S4", - "S5", - "S6", - "S7", - "S8" - ], + "cname": { + "$ref": "#/definitions/cnameType", "metadata": { - "description": "Required. The SKU of the Cognitive Services account. Use 'Get-AzCognitiveServicesAccountSku' to determine a valid combinations of 'kind' and 'SKU' for your Azure region." + "description": "Optional. Array of CNAME records." } }, - "category": { - "type": "string", - "defaultValue": "CognitiveService", + "mx": { + "$ref": "#/definitions/mxType", "metadata": { - "description": "Category of the Cognitive Services account." + "description": "Optional. Array of MX records." } }, - "networkIsolation": { - "type": "bool", + "ptr": { + "$ref": "#/definitions/ptrType", "metadata": { - "description": "Specifies whether to enable network isolation. If true, the resource will be deployed in a private endpoint and public network access will be disabled." + "description": "Optional. Array of PTR records." } }, - "privateDnsZonesResourceIds": { - "type": "array", - "items": { - "type": "string" - }, - "defaultValue": [], + "soa": { + "$ref": "#/definitions/soaType", "metadata": { - "description": "Existing resource ID of the private DNS zone for the private endpoint." + "description": "Optional. Array of SOA records." } }, - "virtualNetworkSubnetResourceId": { - "type": "string", + "srv": { + "$ref": "#/definitions/srvType", "metadata": { - "description": "Resource ID of the subnet for the private endpoint." + "description": "Optional. Array of SRV records." } }, - "logAnalyticsWorkspaceResourceId": { - "type": "string", + "txt": { + "$ref": "#/definitions/txtType", "metadata": { - "description": "The resource ID of the Log Analytics workspace to use for diagnostic settings." + "description": "Optional. Array of TXT records." } }, - "aiModelDeployments": { - "type": "array", - "items": { - "$ref": "#/definitions/deploymentsType" - }, - "defaultValue": [], + "virtualNetworkLinks": { + "$ref": "#/definitions/virtualNetworkLinkType", "metadata": { - "description": "Optional. Specifies the OpenAI deployments to create." + "description": "Optional. Array of custom objects describing vNet links of the DNS zone. Each object should contain properties 'virtualNetworkResourceId' and 'registrationEnabled'. The 'vnetResourceId' is a resource ID of a vNet to link, 'registrationEnabled' (bool) enables automatic DNS registration in the zone for the linked vNet." + } + }, + "location": { + "type": "string", + "defaultValue": "global", + "metadata": { + "description": "Optional. The location of the PrivateDNSZone. Should be global." } }, "roleAssignments": { - "type": "array", - "items": { - "$ref": "#/definitions/roleAssignmentType" - }, - "nullable": true, + "$ref": "#/definitions/roleAssignmentType", "metadata": { "description": "Optional. Array of role assignments to create." } }, "tags": { "type": "object", - "defaultValue": {}, + "nullable": true, "metadata": { - "description": "Optional. Tags to be applied to the resources." + "description": "Optional. Tags of the resource." } }, - "networkAcls": { - "type": "object", + "lock": { + "$ref": "#/definitions/lockType", "metadata": { - "description": "Optional. A collection of rules governing the accessibility from specific network locations." + "description": "Optional. The lock settings of the service." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." } } }, "variables": { "copy": [ { - "name": "privateDnsZones", - "count": "[length(parameters('privateDnsZonesResourceIds'))]", - "input": { - "privateDnsZoneResourceId": "[parameters('privateDnsZonesResourceIds')[copyIndex('privateDnsZones')]]" - } + "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)))))]" } ], - "nameFormatted": "[take(toLower(parameters('name')), 24)]" + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "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": { - "cognitiveService": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privatednszone.{0}.{1}', replace('0.7.0', '.', '-'), 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" + } + } + } + } + }, + "privateDnsZone": { + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]" + }, + "privateDnsZone_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/privateDnsZones/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "privateDnsZone_roleAssignments": { + "copy": { + "name": "privateDnsZone_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones', 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": [ + "privateDnsZone" + ] + }, + "privateDnsZone_A": { + "copy": { + "name": "privateDnsZone_A", + "count": "[length(coalesce(parameters('a'), createArray()))]" + }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[take(format('cog-{0}-{1}-deployment', parameters('kind'), parameters('name')), 64)]", + "name": "[format('{0}-PrivateDnsZone-ARecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "name": { - "value": "[variables('nameFormatted')]" - }, - "location": { - "value": "[parameters('location')]" - }, - "tags": { - "value": "[parameters('tags')]" - }, - "sku": { - "value": "[parameters('sku')]" - }, - "kind": { - "value": "[parameters('kind')]" - }, - "allowProjectManagement": { - "value": true - }, - "managedIdentities": { - "value": { - "systemAssigned": true - } - }, - "deployments": { - "value": "[parameters('aiModelDeployments')]" - }, - "customSubDomainName": { + "privateDnsZoneName": { "value": "[parameters('name')]" }, - "disableLocalAuth": { - "value": "[parameters('networkIsolation')]" + "name": { + "value": "[coalesce(parameters('a'), createArray())[copyIndex()].name]" }, - "publicNetworkAccess": "[if(parameters('networkIsolation'), createObject('value', 'Disabled'), createObject('value', 'Enabled'))]", - "diagnosticSettings": { - "value": [ - { - "workspaceResourceId": "[parameters('logAnalyticsWorkspaceResourceId')]" - } - ] + "aRecords": { + "value": "[tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'aRecords')]" }, - "roleAssignments": { - "value": "[parameters('roleAssignments')]" + "metadata": { + "value": "[tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'metadata')]" }, - "networkAcls": { - "value": "[parameters('networkAcls')]" + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'ttl'), 3600)]" }, - "privateEndpoints": "[if(parameters('networkIsolation'), createObject('value', createArray(createObject('privateDnsZoneGroup', createObject('privateDnsZoneGroupConfigs', variables('privateDnsZones')), 'subnetResourceId', parameters('virtualNetworkSubnetResourceId')))), createObject('value', createArray()))]" + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'roleAssignments')]" + } }, "template": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", @@ -53375,1057 +62238,1376 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "16135659971302525380" + "version": "0.32.4.45862", + "templateHash": "2531120132215940282" }, - "name": "Cognitive Services", - "description": "This module deploys a Cognitive Service." + "name": "Private DNS Zone A record", + "description": "This module deploys a Private DNS Zone A record.", + "owner": "Azure/module-maintainers" }, "definitions": { - "privateEndpointOutputType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the private endpoint." - } - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the private endpoint." - } - }, - "groupId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "The group Id for the private endpoint Group." - } - }, - "customDnsConfigs": { - "type": "array", - "items": { - "type": "object", - "properties": { - "fqdn": { - "type": "string", - "nullable": true, - "metadata": { - "description": "FQDN that resolves to private endpoint IP address." - } - }, - "ipAddresses": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "A list of private IP addresses of the private endpoint." - } - } + "roleAssignmentType": { + "type": "array", + "items": { + "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." } }, - "metadata": { - "description": "The custom DNS configurations of the private endpoint." - } - }, - "networkInterfaceResourceIds": { - "type": "array", - "items": { - "type": "string" + "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." + } }, - "metadata": { - "description": "The IDs of the network interfaces associated with the private endpoint." - } - } - }, - "metadata": { - "__bicep_export!": true, - "description": "The type for the private endpoint output." - } - }, - "deploymentType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Specify the name of cognitive service account deployment." - } - }, - "model": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of Cognitive Services account deployment model." - } - }, - "format": { - "type": "string", - "metadata": { - "description": "Required. The format of Cognitive Services account deployment model." - } - }, - "version": { - "type": "string", - "metadata": { - "description": "Required. The version of Cognitive Services account deployment model." - } + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." } }, - "metadata": { - "description": "Required. Properties of Cognitive Services account deployment model." - } - }, - "sku": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the resource model definition representing SKU." - } - }, - "capacity": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The capacity of the resource model definition representing SKU." - } - }, - "tier": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The tier of the resource model definition representing SKU." - } - }, - "size": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The size of the resource model definition representing SKU." - } - }, - "family": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The family of the resource model definition representing SKU." - } + "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\"." } }, - "nullable": true, - "metadata": { - "description": "Optional. The resource model definition representing SKU." - } - }, - "raiPolicyName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of RAI policy." - } - }, - "versionUpgradeOption": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The version upgrade option." + "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." + } } } }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", "metadata": { - "__bicep_export!": true, - "description": "The type for a cognitive services account deployment." + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." } }, - "endpointType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Type of the endpoint." - } - }, - "endpoint": { - "type": "string", - "nullable": true, - "metadata": { - "description": "The endpoint URI." - } - } - }, + "name": { + "type": "string", "metadata": { - "__bicep_export!": true, - "description": "The type for a cognitive services account endpoint." + "description": "Required. The name of the A record." } }, - "secretsExportConfigurationType": { - "type": "object", - "properties": { - "keyVaultResourceId": { - "type": "string", - "metadata": { - "description": "Required. The key vault name where to store the keys and connection strings generated by the modules." - } - }, - "accessKey1Name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name for the accessKey1 secret to create." - } - }, - "accessKey2Name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name for the accessKey2 secret to create." - } - } - }, + "aRecords": { + "type": "array", + "nullable": true, "metadata": { - "__bicep_export!": true, - "description": "The type of the secrets exported to the provided Key Vault." + "description": "Optional. The list of A records in the record set." } }, - "_1.privateEndpointCustomDnsConfigType": { + "metadata": { "type": "object", - "properties": { - "fqdn": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. FQDN that resolves to private endpoint IP address." - } - }, - "ipAddresses": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. A list of private IP addresses of the private endpoint." - } - } - }, + "nullable": true, "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } + "description": "Optional. The metadata attached to the record set." } }, - "_1.privateEndpointIpConfigurationType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the resource that is unique within a resource group." - } - }, - "properties": { - "type": "object", - "properties": { - "groupId": { - "type": "string", - "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." - } - }, - "memberName": { - "type": "string", - "metadata": { - "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to." - } - }, - "privateIPAddress": { - "type": "string", - "metadata": { - "description": "Required. A private IP address obtained from the private endpoint's subnet." - } - } - }, - "metadata": { - "description": "Required. Properties of private endpoint IP configurations." - } - } - }, + "ttl": { + "type": "int", + "defaultValue": 3600, "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } + "description": "Optional. The TTL (time-to-live) of the records in the record set." } }, - "_1.privateEndpointPrivateDnsZoneGroupType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the Private DNS Zone Group." - } - }, - "privateDnsZoneGroupConfigs": { - "type": "array", - "items": { - "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": "Required. The private DNS Zone Groups to associate the Private Endpoint. A DNS Zone Group can support up to 5 DNS zones." - } - } - }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } + "description": "Optional. Array of role assignments to create." + } + } + }, + "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')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" }, - "_1.secretSetOutputType": { - "type": "object", + "A": { + "type": "Microsoft.Network/privateDnsZones/A", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", "properties": { - "secretResourceId": { - "type": "string", - "metadata": { - "description": "The resourceId of the exported secret." - } - }, - "secretUri": { - "type": "string", - "metadata": { - "description": "The secret URI of the exported secret." - } - }, - "secretUriWithVersion": { - "type": "string", - "metadata": { - "description": "The secret URI with version of the exported secret." - } - } - }, - "metadata": { - "description": "An AVM-aligned type for the output of the secret set via the secrets export feature.", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } + "aRecords": "[parameters('aRecords')]", + "metadata": "[parameters('metadata')]", + "ttl": "[parameters('ttl')]" } }, - "customerManagedKeyType": { - "type": "object", + "A_roleAssignments": { + "copy": { + "name": "A_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/A/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/A', parameters('privateDnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "keyVaultResourceId": { - "type": "string", - "metadata": { - "description": "Required. The resource ID of a key vault to reference a customer managed key for encryption from." - } - }, - "keyName": { - "type": "string", - "metadata": { - "description": "Required. The name of the customer managed key to use for encryption." - } - }, - "keyVersion": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The version of the customer managed key to reference for encryption. If not provided, the deployment will use the latest version available at deployment time." - } - }, - "userAssignedIdentityResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use." - } - } + "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": [ + "A" + ] + } + }, + "outputs": { + "name": { + "type": "string", "metadata": { - "description": "An AVM-aligned type for a customer-managed key. To be used if the resource type does not support auto-rotation of the customer-managed key.", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } - } + "description": "The name of the deployed A record." + }, + "value": "[parameters('name')]" }, - "diagnosticSettingFullType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the diagnostic setting." - } - }, - "logCategoriesAndGroups": { - "type": "array", - "items": { - "type": "object", - "properties": { - "category": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." - } - }, - "categoryGroup": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." - } - }, - "enabled": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Enable or disable the category explicitly. Default is `true`." - } - } + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed A record." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/A', parameters('privateDnsZoneName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed A record." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "privateDnsZone_AAAA": { + "copy": { + "name": "privateDnsZone_AAAA", + "count": "[length(coalesce(parameters('aaaa'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-AAAARecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('aaaa'), createArray())[copyIndex()].name]" + }, + "aaaaRecords": { + "value": "[tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'aaaaRecords')]" + }, + "metadata": { + "value": "[tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'metadata')]" + }, + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'ttl'), 3600)]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'roleAssignments')]" + } + }, + "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.32.4.45862", + "templateHash": "16709340450244912125" + }, + "name": "Private DNS Zone AAAA record", + "description": "This module deploys a Private DNS Zone AAAA record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "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." } }, - "nullable": true, - "metadata": { - "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." - } - }, - "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`." - } - } + "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'." } }, - "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." + "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." + } } } }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", "metadata": { - "description": "An AVM-aligned type for a diagnostic setting. To be used if both logs & metrics are supported by the resource provider.", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." } }, - "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." - } - } - }, + "name": { + "type": "string", "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.5.1" - } + "description": "Required. The name of the AAAA record." } }, - "managedIdentityAllType": { - "type": "object", - "properties": { - "systemAssigned": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Enables system assigned managed identity on the resource." - } - }, - "userAssignedResourceIds": { - "type": "array", - "items": { - "type": "string" - }, - "nullable": true, - "metadata": { - "description": "Optional. The resource ID(s) to assign to the resource. Required if a user assigned identity is used for encryption." - } - } - }, + "aaaaRecords": { + "type": "array", + "nullable": true, "metadata": { - "description": "An AVM-aligned type for a managed identity configuration. To be used if both a system-assigned & user-assigned identities are supported by the resource provider.", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } + "description": "Optional. The list of AAAA records in the record set." } }, - "privateEndpointSingleServiceType": { + "metadata": { "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "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')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "AAAA": { + "type": "Microsoft.Network/privateDnsZones/AAAA", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the Private Endpoint." - } - }, - "location": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The location to deploy the Private Endpoint to." - } - }, - "privateLinkServiceConnectionName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the private link connection to create." - } - }, - "service": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The subresource to deploy the Private Endpoint for. For example \"vault\" for a Key Vault Private Endpoint." - } - }, - "subnetResourceId": { - "type": "string", - "metadata": { - "description": "Required. Resource ID of the subnet where the endpoint needs to be created." - } - }, - "resourceGroupResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The resource ID of the Resource Group the Private Endpoint will be created in. If not specified, the Resource Group of the provided Virtual Network Subnet is used." - } - }, - "privateDnsZoneGroup": { - "$ref": "#/definitions/_1.privateEndpointPrivateDnsZoneGroupType", - "nullable": true, - "metadata": { - "description": "Optional. The private DNS Zone Group to configure for the Private Endpoint." - } - }, - "isManualConnection": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. If Manual Private Link Connection is required." - } - }, - "manualConnectionRequestMessage": { - "type": "string", - "nullable": true, - "maxLength": 140, - "metadata": { - "description": "Optional. A message passed to the owner of the remote resource with the manual connection request." - } - }, - "customDnsConfigs": { - "type": "array", - "items": { - "$ref": "#/definitions/_1.privateEndpointCustomDnsConfigType" + "aaaaRecords": "[parameters('aaaaRecords')]", + "metadata": "[parameters('metadata')]", + "ttl": "[parameters('ttl')]" + } + }, + "AAAA_roleAssignments": { + "copy": { + "name": "AAAA_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/AAAA/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/AAAA', parameters('privateDnsZoneName'), 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": [ + "AAAA" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed AAAA record." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed AAAA record." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/AAAA', parameters('privateDnsZoneName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed AAAA record." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "privateDnsZone_CNAME": { + "copy": { + "name": "privateDnsZone_CNAME", + "count": "[length(coalesce(parameters('cname'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-CNAMERecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('cname'), createArray())[copyIndex()].name]" + }, + "cnameRecord": { + "value": "[tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'cnameRecord')]" + }, + "metadata": { + "value": "[tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'metadata')]" + }, + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'ttl'), 3600)]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'roleAssignments')]" + } + }, + "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.32.4.45862", + "templateHash": "9976020649752073181" + }, + "name": "Private DNS Zone CNAME record", + "description": "This module deploys a Private DNS Zone CNAME record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "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." + } }, - "nullable": true, - "metadata": { - "description": "Optional. Custom DNS configurations." - } - }, - "ipConfigurations": { - "type": "array", - "items": { - "$ref": "#/definitions/_1.privateEndpointIpConfigurationType" + "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'." + } }, - "nullable": true, - "metadata": { - "description": "Optional. A list of IP configurations of the Private Endpoint. This will be used to map to the first-party Service endpoints." - } - }, - "applicationSecurityGroupResourceIds": { - "type": "array", - "items": { - "type": "string" + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } }, - "nullable": true, - "metadata": { - "description": "Optional. Application security groups in which the Private Endpoint IP configuration is included." - } - }, - "customNetworkInterfaceName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The custom name of the network interface attached to the Private Endpoint." - } - }, - "lock": { - "$ref": "#/definitions/lockType", - "nullable": true, - "metadata": { - "description": "Optional. Specify the type of lock." - } - }, - "roleAssignments": { - "type": "array", - "items": { - "$ref": "#/definitions/roleAssignmentType" + "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." + } }, - "nullable": true, - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "tags": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Tags to be applied on all resources/Resource Groups in this deployment." - } - }, - "enableTelemetry": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Enable/Disable usage telemetry for module." + "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." + } } } }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", "metadata": { - "description": "An AVM-aligned type for a private endpoint. To be used if the private endpoint's default service / groupId can be assumed (i.e., for services that only have one Private Endpoint type like 'vault' for key vault).", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." } }, - "roleAssignmentType": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the CNAME record." + } + }, + "cnameRecord": { "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." - } - } - }, + "nullable": true, "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.5.1" - } + "description": "Optional. A CNAME record." } }, - "secretsOutputType": { + "metadata": { "type": "object", - "properties": {}, - "additionalProperties": { - "$ref": "#/definitions/_1.secretSetOutputType", - "metadata": { - "description": "An exported secret's references." - } - }, + "nullable": true, "metadata": { - "description": "A map of the exported secrets", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } + "description": "Optional. The metadata attached to the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." } } }, - "parameters": { + "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')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "CNAME": { + "type": "Microsoft.Network/privateDnsZones/CNAME", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "properties": { + "cnameRecord": "[parameters('cnameRecord')]", + "metadata": "[parameters('metadata')]", + "ttl": "[parameters('ttl')]" + } + }, + "CNAME_roleAssignments": { + "copy": { + "name": "CNAME_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/CNAME/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/CNAME', parameters('privateDnsZoneName'), 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": [ + "CNAME" + ] + } + }, + "outputs": { "name": { "type": "string", "metadata": { - "description": "Required. The name of Cognitive Services account." - } + "description": "The name of the deployed CNAME record." + }, + "value": "[parameters('name')]" }, - "kind": { + "resourceId": { "type": "string", - "allowedValues": [ - "AIServices", - "AnomalyDetector", - "CognitiveServices", - "ComputerVision", - "ContentModerator", - "ContentSafety", - "ConversationalLanguageUnderstanding", - "CustomVision.Prediction", - "CustomVision.Training", - "Face", - "FormRecognizer", - "HealthInsights", - "ImmersiveReader", - "Internal.AllInOne", - "LUIS", - "LUIS.Authoring", - "LanguageAuthoring", - "MetricsAdvisor", - "OpenAI", - "Personalizer", - "QnAMaker.v2", - "SpeechServices", - "TextAnalytics", - "TextTranslation" - ], "metadata": { - "description": "Required. Kind of the Cognitive Services account. Use 'Get-AzCognitiveServicesAccountSku' to determine a valid combinations of 'kind' and 'SKU' for your Azure region." - } + "description": "The resource ID of the deployed CNAME record." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/CNAME', parameters('privateDnsZoneName'), parameters('name'))]" }, - "sku": { + "resourceGroupName": { "type": "string", - "defaultValue": "S0", - "allowedValues": [ - "C2", - "C3", - "C4", - "F0", - "F1", - "S", - "S0", - "S1", - "S10", - "S2", - "S3", - "S4", - "S5", - "S6", - "S7", - "S8", - "S9" - ], "metadata": { - "description": "Optional. SKU of the Cognitive Services account. Use 'Get-AzCognitiveServicesAccountSku' to determine a valid combinations of 'kind' and 'SKU' for your Azure region." + "description": "The resource group of the deployed CNAME record." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "privateDnsZone_MX": { + "copy": { + "name": "privateDnsZone_MX", + "count": "[length(coalesce(parameters('mx'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-MXRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('mx'), createArray())[copyIndex()].name]" + }, + "metadata": { + "value": "[tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'metadata')]" + }, + "mxRecords": { + "value": "[tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'mxRecords')]" + }, + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'ttl'), 3600)]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'roleAssignments')]" + } + }, + "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.32.4.45862", + "templateHash": "2520323624213076361" + }, + "name": "Private DNS Zone MX record", + "description": "This module deploys a Private DNS Zone MX record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "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." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." } }, - "location": { + "name": { "type": "string", - "defaultValue": "[resourceGroup().location]", "metadata": { - "description": "Optional. Location for all Resources." + "description": "Required. The name of the MX record." } }, - "diagnosticSettings": { + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "mxRecords": { "type": "array", - "items": { - "$ref": "#/definitions/diagnosticSettingFullType" - }, "nullable": true, "metadata": { - "description": "Optional. The diagnostic settings of the service." + "description": "Optional. The list of MX records in the record set." } }, - "publicNetworkAccess": { + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "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')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "MX": { + "type": "Microsoft.Network/privateDnsZones/MX", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "properties": { + "metadata": "[parameters('metadata')]", + "mxRecords": "[parameters('mxRecords')]", + "ttl": "[parameters('ttl')]" + } + }, + "MX_roleAssignments": { + "copy": { + "name": "MX_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/MX/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/MX', parameters('privateDnsZoneName'), 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": [ + "MX" + ] + } + }, + "outputs": { + "name": { "type": "string", - "nullable": true, - "allowedValues": [ - "Enabled", - "Disabled" - ], "metadata": { - "description": "Optional. Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set and networkAcls are not set." - } + "description": "The name of the deployed MX record." + }, + "value": "[parameters('name')]" }, - "customSubDomainName": { + "resourceId": { "type": "string", - "nullable": true, "metadata": { - "description": "Conditional. Subdomain name used for token-based authentication. Required if 'networkAcls' or 'privateEndpoints' are set." - } + "description": "The resource ID of the deployed MX record." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/MX', parameters('privateDnsZoneName'), parameters('name'))]" }, - "networkAcls": { - "type": "object", - "nullable": true, + "resourceGroupName": { + "type": "string", "metadata": { - "description": "Optional. A collection of rules governing the accessibility from specific network locations." - } + "description": "The resource group of the deployed MX record." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "privateDnsZone_PTR": { + "copy": { + "name": "privateDnsZone_PTR", + "count": "[length(coalesce(parameters('ptr'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-PTRRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('ptr'), createArray())[copyIndex()].name]" + }, + "metadata": { + "value": "[tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'metadata')]" + }, + "ptrRecords": { + "value": "[tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'ptrRecords')]" + }, + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'ttl'), 3600)]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'roleAssignments')]" + } + }, + "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.32.4.45862", + "templateHash": "3080404733048745471" }, - "privateEndpoints": { + "name": "Private DNS Zone PTR record", + "description": "This module deploys a Private DNS Zone PTR record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { "type": "array", "items": { - "$ref": "#/definitions/privateEndpointSingleServiceType" + "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." + } + } + } }, - "nullable": true, - "metadata": { - "description": "Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible." - } - }, - "lock": { - "$ref": "#/definitions/lockType", - "nullable": true, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", "metadata": { - "description": "Optional. The lock settings of the service." + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." } }, - "roleAssignments": { - "type": "array", - "items": { - "$ref": "#/definitions/roleAssignmentType" - }, - "nullable": true, + "name": { + "type": "string", "metadata": { - "description": "Optional. Array of role assignments to create." + "description": "Required. The name of the PTR record." } }, - "tags": { + "metadata": { "type": "object", "nullable": true, "metadata": { - "description": "Optional. Tags of the resource." + "description": "Optional. The metadata attached to the record set." } }, - "allowedFqdnList": { + "ptrRecords": { "type": "array", "nullable": true, "metadata": { - "description": "Optional. List of allowed FQDN." + "description": "Optional. The list of PTR records in the record set." } }, - "apiProperties": { - "type": "object", - "nullable": true, + "ttl": { + "type": "int", + "defaultValue": 3600, "metadata": { - "description": "Optional. The API properties for special APIs." + "description": "Optional. The TTL (time-to-live) of the records in the record set." } }, - "disableLocalAuth": { - "type": "bool", - "defaultValue": true, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", "metadata": { - "description": "Optional. Allow only Azure AD authentication. Should be enabled for security reasons." + "description": "Optional. Array of role assignments to create." } - }, - "customerManagedKey": { - "$ref": "#/definitions/customerManagedKeyType", - "nullable": true, - "metadata": { - "description": "Optional. The customer managed key definition." + } + }, + "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')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" }, - "dynamicThrottlingEnabled": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "Optional. The flag to enable dynamic throttling." + "PTR": { + "type": "Microsoft.Network/privateDnsZones/PTR", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "properties": { + "metadata": "[parameters('metadata')]", + "ptrRecords": "[parameters('ptrRecords')]", + "ttl": "[parameters('ttl')]" } }, - "migrationToken": { - "type": "securestring", - "nullable": true, + "PTR_roleAssignments": { + "copy": { + "name": "PTR_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/PTR/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/PTR', parameters('privateDnsZoneName'), 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": [ + "PTR" + ] + } + }, + "outputs": { + "name": { + "type": "string", "metadata": { - "description": "Optional. Resource migration token." - } + "description": "The name of the deployed PTR record." + }, + "value": "[parameters('name')]" }, - "restore": { - "type": "bool", - "defaultValue": false, + "resourceId": { + "type": "string", "metadata": { - "description": "Optional. Restore a soft-deleted cognitive service at deployment time. Will fail if no such soft-deleted resource exists." - } + "description": "The resource ID of the deployed PTR record." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/PTR', parameters('privateDnsZoneName'), parameters('name'))]" }, - "restrictOutboundNetworkAccess": { - "type": "bool", - "defaultValue": true, + "resourceGroupName": { + "type": "string", "metadata": { - "description": "Optional. Restrict outbound network access." - } + "description": "The resource group of the deployed PTR record." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "privateDnsZone_SOA": { + "copy": { + "name": "privateDnsZone_SOA", + "count": "[length(coalesce(parameters('soa'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-SOARecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('soa'), createArray())[copyIndex()].name]" + }, + "metadata": { + "value": "[tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'metadata')]" + }, + "soaRecord": { + "value": "[tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'soaRecord')]" + }, + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'ttl'), 3600)]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'roleAssignments')]" + } + }, + "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.32.4.45862", + "templateHash": "6653951445614700931" }, - "userOwnedStorage": { + "name": "Private DNS Zone SOA record", + "description": "This module deploys a Private DNS Zone SOA record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { "type": "array", - "nullable": true, + "items": { + "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." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", "metadata": { - "description": "Optional. The storage accounts for this resource." + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." } }, - "managedIdentities": { - "$ref": "#/definitions/managedIdentityAllType", - "nullable": true, + "name": { + "type": "string", "metadata": { - "description": "Optional. The managed identity definition for this resource." + "description": "Required. The name of the SOA record." } }, - "enableTelemetry": { - "type": "bool", - "defaultValue": true, + "metadata": { + "type": "object", + "nullable": true, "metadata": { - "description": "Optional. Enable/Disable usage telemetry for module." + "description": "Optional. The metadata attached to the record set." } }, - "deployments": { - "type": "array", - "items": { - "$ref": "#/definitions/deploymentType" - }, + "soaRecord": { + "type": "object", "nullable": true, "metadata": { - "description": "Optional. Array of deployments about cognitive service accounts to create." + "description": "Optional. A SOA record." } }, - "secretsExportConfiguration": { - "$ref": "#/definitions/secretsExportConfigurationType", - "nullable": true, + "ttl": { + "type": "int", + "defaultValue": 3600, "metadata": { - "description": "Optional. Key vault reference and secret settings for the module's secrets export." + "description": "Optional. The TTL (time-to-live) of the records in the record set." } }, - "allowProjectManagement": { - "type": "bool", - "nullable": true, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", "metadata": { - "description": "Optional. Enable/Disable project management feature for AI Foundry." + "description": "Optional. Array of role assignments to create." } } }, @@ -54433,40 +63615,14 @@ "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)))))]" - } - ], - "enableReferencedModulesTelemetry": false, - "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", - "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned, UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', null())), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", - "builtInRoleNames": { - "Cognitive Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '25fbc0a9-bd7c-42a3-aa1a-3b75d497ee68')]", - "Cognitive Services Custom Vision Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c1ff6cc2-c111-46fe-8896-e0ef812ad9f3')]", - "Cognitive Services Custom Vision Deployment": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5c4089e1-6d96-4d2f-b296-c1bc7137275f')]", - "Cognitive Services Custom Vision Labeler": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '88424f51-ebe7-446f-bc41-7fa16989e96c')]", - "Cognitive Services Custom Vision Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '93586559-c37d-4a6b-ba08-b9f0940c2d73')]", - "Cognitive Services Custom Vision Trainer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0a5ae4ab-0d65-4eeb-be61-29fc9b54394b')]", - "Cognitive Services Data Reader (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b59867f0-fa02-499b-be73-45a86b5b3e1c')]", - "Cognitive Services Face Recognizer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9894cab4-e18a-44aa-828b-cb588cd6f2d7')]", - "Cognitive Services Immersive Reader User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b2de6794-95db-4659-8781-7e080d3f2b9d')]", - "Cognitive Services Language Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f07febfe-79bc-46b1-8b37-790e26e6e498')]", - "Cognitive Services Language Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7628b7b8-a8b2-4cdc-b46f-e9b35248918e')]", - "Cognitive Services Language Writer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f2310ca1-dc64-4889-bb49-c8e0fa3d47a8')]", - "Cognitive Services LUIS Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f72c8140-2111-481c-87ff-72b910f6e3f8')]", - "Cognitive Services LUIS Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18e81cdc-4e98-4e29-a639-e7d10c5a6226')]", - "Cognitive Services LUIS Writer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6322a993-d5c9-4bed-b113-e49bbea25b27')]", - "Cognitive Services Metrics Advisor Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'cb43c632-a144-4ec5-977c-e80c4affc34a')]", - "Cognitive Services Metrics Advisor User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3b20f47b-3825-43cb-8114-4bd2201156a8')]", - "Cognitive Services OpenAI Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a001fd3d-188f-4b5d-821b-7da978bf7442')]", - "Cognitive Services OpenAI User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e0bd9bd-7b93-4f28-af87-19fc36ad61bd')]", - "Cognitive Services QnA Maker Editor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f4cc2bf9-21be-47a1-bdf1-5c5804381025')]", - "Cognitive Services QnA Maker Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '466ccd10-b268-4a11-b098-b4849f024126')]", - "Cognitive Services Speech Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0e75ca1e-0464-4b4d-8b93-68208a576181')]", - "Cognitive Services Speech User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f2dc8367-1007-4938-bd23-fe263f013447')]", - "Cognitive Services User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a97b65f3-24c7-4388-baec-2e87135dc908')]", - "Azure AI Developer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '64702f94-c441-49e6-a78b-ef80e0188fee')]", + "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')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", @@ -54474,169 +63630,31 @@ } }, "resources": { - "cMKKeyVault::cMKKey": { - "condition": "[and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), not(empty(tryGet(parameters('customerManagedKey'), 'keyName')))))]", + "privateDnsZone": { "existing": true, - "type": "Microsoft.KeyVault/vaults/keys", - "apiVersion": "2023-07-01", - "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[2]]", - "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[4]]", - "name": "[format('{0}/{1}', last(split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')), tryGet(parameters('customerManagedKey'), 'keyName'))]" + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" }, - "avmTelemetry": { - "condition": "[parameters('enableTelemetry')]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2024-03-01", - "name": "[format('46d3xbcp.res.cognitiveservices-account.{0}.{1}', replace('0.11.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "SOA": { + "type": "Microsoft.Network/privateDnsZones/SOA", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", "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" - } - } - } + "metadata": "[parameters('metadata')]", + "soaRecord": "[parameters('soaRecord')]", + "ttl": "[parameters('ttl')]" } }, - "cMKKeyVault": { - "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId')))]", - "existing": true, - "type": "Microsoft.KeyVault/vaults", - "apiVersion": "2023-07-01", - "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[2]]", - "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[4]]", - "name": "[last(split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/'))]" - }, - "cMKUserAssignedIdentity": { - "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId')))]", - "existing": true, - "type": "Microsoft.ManagedIdentity/userAssignedIdentities", - "apiVersion": "2025-01-31-preview", - "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/')[2]]", - "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/')[4]]", - "name": "[last(split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/'))]" - }, - "cognitiveService": { - "type": "Microsoft.CognitiveServices/accounts", - "apiVersion": "2025-04-01-preview", - "name": "[parameters('name')]", - "kind": "[parameters('kind')]", - "identity": "[variables('identity')]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", - "sku": { - "name": "[parameters('sku')]" - }, - "properties": { - "allowProjectManagement": "[parameters('allowProjectManagement')]", - "customSubDomainName": "[parameters('customSubDomainName')]", - "networkAcls": "[if(not(empty(coalesce(parameters('networkAcls'), createObject()))), createObject('defaultAction', tryGet(parameters('networkAcls'), 'defaultAction'), 'virtualNetworkRules', coalesce(tryGet(parameters('networkAcls'), 'virtualNetworkRules'), createArray()), 'ipRules', coalesce(tryGet(parameters('networkAcls'), 'ipRules'), createArray())), null())]", - "publicNetworkAccess": "[if(not(equals(parameters('publicNetworkAccess'), null())), parameters('publicNetworkAccess'), if(not(empty(parameters('networkAcls'))), 'Enabled', 'Disabled'))]", - "allowedFqdnList": "[parameters('allowedFqdnList')]", - "apiProperties": "[parameters('apiProperties')]", - "disableLocalAuth": "[parameters('disableLocalAuth')]", - "encryption": "[if(not(empty(parameters('customerManagedKey'))), createObject('keySource', 'Microsoft.KeyVault', 'keyVaultProperties', createObject('identityClientId', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), ''))), reference('cMKUserAssignedIdentity').clientId, null()), 'keyVaultUri', reference('cMKKeyVault').vaultUri, 'keyName', parameters('customerManagedKey').keyName, 'keyVersion', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'keyVersion'), ''))), tryGet(parameters('customerManagedKey'), 'keyVersion'), last(split(reference('cMKKeyVault::cMKKey').keyUriWithVersion, '/'))))), null())]", - "migrationToken": "[parameters('migrationToken')]", - "restore": "[parameters('restore')]", - "restrictOutboundNetworkAccess": "[parameters('restrictOutboundNetworkAccess')]", - "userOwnedStorage": "[parameters('userOwnedStorage')]", - "dynamicThrottlingEnabled": "[parameters('dynamicThrottlingEnabled')]" - }, - "dependsOn": [ - "cMKKeyVault", - "cMKKeyVault::cMKKey", - "cMKUserAssignedIdentity" - ] - }, - "cognitiveService_deployments": { - "copy": { - "name": "cognitiveService_deployments", - "count": "[length(coalesce(parameters('deployments'), createArray()))]", - "mode": "serial", - "batchSize": 1 - }, - "type": "Microsoft.CognitiveServices/accounts/deployments", - "apiVersion": "2025-04-01-preview", - "name": "[format('{0}/{1}', parameters('name'), coalesce(tryGet(coalesce(parameters('deployments'), createArray())[copyIndex()], 'name'), format('{0}-deployments', parameters('name'))))]", - "properties": { - "model": "[coalesce(parameters('deployments'), createArray())[copyIndex()].model]", - "raiPolicyName": "[tryGet(coalesce(parameters('deployments'), createArray())[copyIndex()], 'raiPolicyName')]", - "versionUpgradeOption": "[tryGet(coalesce(parameters('deployments'), createArray())[copyIndex()], 'versionUpgradeOption')]" - }, - "sku": "[coalesce(tryGet(coalesce(parameters('deployments'), createArray())[copyIndex()], 'sku'), createObject('name', parameters('sku'), 'capacity', tryGet(parameters('sku'), 'capacity'), 'tier', tryGet(parameters('sku'), 'tier'), 'size', tryGet(parameters('sku'), 'size'), 'family', tryGet(parameters('sku'), 'family')))]", - "dependsOn": [ - "cognitiveService" - ] - }, - "cognitiveService_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.CognitiveServices/accounts/{0}', parameters('name'))]", - "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", - "properties": { - "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", - "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" - }, - "dependsOn": [ - "cognitiveService" - ] - }, - "cognitiveService_diagnosticSettings": { - "copy": { - "name": "cognitiveService_diagnosticSettings", - "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" - }, - "type": "Microsoft.Insights/diagnosticSettings", - "apiVersion": "2021-05-01-preview", - "scope": "[format('Microsoft.CognitiveServices/accounts/{0}', parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", - "properties": { - "copy": [ - { - "name": "metrics", - "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", - "input": { - "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", - "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", - "timeGrain": null - } - }, - { - "name": "logs", - "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", - "input": { - "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", - "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", - "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" - } - } - ], - "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", - "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", - "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", - "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", - "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", - "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" - }, - "dependsOn": [ - "cognitiveService" - ] - }, - "cognitiveService_roleAssignments": { + "SOA_roleAssignments": { "copy": { - "name": "cognitiveService_roleAssignments", + "name": "SOA_roleAssignments", "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.CognitiveServices/accounts/{0}', parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/SOA/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/SOA', parameters('privateDnsZoneName'), 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]", @@ -54647,784 +63665,2344 @@ "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" }, "dependsOn": [ - "cognitiveService" + "SOA" ] - }, - "cognitiveService_privateEndpoints": { - "copy": { - "name": "cognitiveService_privateEndpoints", - "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]" + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed SOA record." }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-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]]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'name'), format('pep-{0}-{1}-{2}', last(split(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'account'), copyIndex()))]" - }, - "privateLinkServiceConnections": "[if(not(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true())), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'account'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'account')))))), createObject('value', null()))]", - "manualPrivateLinkServiceConnections": "[if(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true()), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'account'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'account')), 'requestMessage', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualConnectionRequestMessage'), 'Manual approval required.'))))), createObject('value', null()))]", - "subnetResourceId": { - "value": "[coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId]" - }, - "enableTelemetry": { - "value": "[variables('enableReferencedModulesTelemetry')]" - }, - "location": { - "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'location'), reference(split(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location)]" - }, - "lock": { - "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" - }, - "privateDnsZoneGroup": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroup')]" - }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'roleAssignments')]" - }, - "tags": { - "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" - }, - "customDnsConfigs": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customDnsConfigs')]" - }, - "ipConfigurations": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'ipConfigurations')]" - }, - "applicationSecurityGroupResourceIds": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'applicationSecurityGroupResourceIds')]" - }, - "customNetworkInterfaceName": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customNetworkInterfaceName')]" - } - }, - "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.34.44.8038", - "templateHash": "12389807800450456797" - }, - "name": "Private Endpoints", - "description": "This module deploys a Private Endpoint." - }, - "definitions": { - "privateDnsZoneGroupType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the Private DNS Zone Group." - } - }, - "privateDnsZoneGroupConfigs": { - "type": "array", - "items": { - "$ref": "#/definitions/privateDnsZoneGroupConfigType" - }, - "metadata": { - "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." - } - } - }, - "metadata": { - "__bicep_export!": true - } - }, - "ipConfigurationType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the resource that is unique within a resource group." - } - }, - "properties": { - "type": "object", - "properties": { - "groupId": { - "type": "string", - "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." - } - }, - "memberName": { - "type": "string", - "metadata": { - "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." - } - }, - "privateIPAddress": { - "type": "string", - "metadata": { - "description": "Required. A private IP address obtained from the private endpoint's subnet." - } - } - }, - "metadata": { - "description": "Required. Properties of private endpoint IP configurations." - } - } - }, - "metadata": { - "__bicep_export!": true - } - }, - "privateLinkServiceConnectionType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the private link service connection." - } - }, - "properties": { - "type": "object", - "properties": { - "groupIds": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." - } - }, - "privateLinkServiceId": { - "type": "string", - "metadata": { - "description": "Required. The resource id of private link service." - } - }, - "requestMessage": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." - } - } - }, - "metadata": { - "description": "Required. Properties of private link service connection." - } - } - }, - "metadata": { - "__bicep_export!": true - } - }, - "customDnsConfigType": { - "type": "object", - "properties": { - "fqdn": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. FQDN that resolves to private endpoint IP address." - } - }, - "ipAddresses": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. A list of private IP addresses of the private endpoint." - } - } - }, - "metadata": { - "__bicep_export!": true - } - }, - "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." - } - } - }, - "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.5.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": { - "__bicep_imported_from!": { - "sourceTemplate": "private-dns-zone-group/main.bicep" - } - } - }, - "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.5.1" - } - } + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed SOA record." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/SOA', parameters('privateDnsZoneName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed SOA record." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "privateDnsZone_SRV": { + "copy": { + "name": "privateDnsZone_SRV", + "count": "[length(coalesce(parameters('srv'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-SRVRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('srv'), createArray())[copyIndex()].name]" + }, + "metadata": { + "value": "[tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'metadata')]" + }, + "srvRecords": { + "value": "[tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'srvRecords')]" + }, + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'ttl'), 3600)]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'roleAssignments')]" + } + }, + "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.32.4.45862", + "templateHash": "5790774778713328446" + }, + "name": "Private DNS Zone SRV record", + "description": "This module deploys a Private DNS Zone SRV record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "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." } }, - "parameters": { - "name": { - "type": "string", - "metadata": { - "description": "Required. Name of the private endpoint resource to create." - } - }, - "subnetResourceId": { - "type": "string", - "metadata": { - "description": "Required. Resource ID of the subnet where the endpoint needs to be created." - } - }, - "applicationSecurityGroupResourceIds": { - "type": "array", - "items": { - "type": "string" - }, - "nullable": true, - "metadata": { - "description": "Optional. Application security groups in which the private endpoint IP configuration is included." - } - }, - "customNetworkInterfaceName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The custom name of the network interface attached to the private endpoint." - } - }, - "ipConfigurations": { - "type": "array", - "items": { - "$ref": "#/definitions/ipConfigurationType" - }, - "nullable": true, - "metadata": { - "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." - } - }, - "privateDnsZoneGroup": { - "$ref": "#/definitions/privateDnsZoneGroupType", - "nullable": true, - "metadata": { - "description": "Optional. The private DNS zone group to configure for the private endpoint." - } - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]", - "metadata": { - "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", - "nullable": true, - "metadata": { - "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." - } - }, - "customDnsConfigs": { - "type": "array", - "items": { - "$ref": "#/definitions/customDnsConfigType" - }, - "nullable": true, - "metadata": { - "description": "Optional. Custom DNS configurations." - } - }, - "manualPrivateLinkServiceConnections": { - "type": "array", - "items": { - "$ref": "#/definitions/privateLinkServiceConnectionType" - }, - "nullable": true, - "metadata": { - "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." - } - }, - "privateLinkServiceConnections": { - "type": "array", - "items": { - "$ref": "#/definitions/privateLinkServiceConnectionType" - }, - "nullable": true, - "metadata": { - "description": "Conditional. A grouping of information about the connection to the remote resource. Required if `manualPrivateLinkServiceConnections` is empty." - } - }, - "enableTelemetry": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Optional. Enable/Disable usage telemetry for module." - } + "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'." } }, - "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)))))]" - } + "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" ], - "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')]" + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." } }, - "resources": { - "avmTelemetry": { - "condition": "[parameters('enableTelemetry')]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2024-03-01", - "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.11.0', '.', '-'), 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-05-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": "[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": "2022-09-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.34.44.8038", - "templateHash": "13997305779829540948" - }, - "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 - } - } - }, - "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." - } - } - }, - "variables": { - "copy": [ - { - "name": "privateDnsZoneConfigsVar", - "count": "[length(parameters('privateDnsZoneConfigs'))]", - "input": { - "name": "[coalesce(tryGet(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')], 'name'), last(split(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId, '/')))]", - "properties": { - "privateDnsZoneId": "[parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId]" - } - } - } - ] - }, - "resources": { - "privateEndpoint": { - "existing": true, - "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2024-05-01", - "name": "[parameters('privateEndpointName')]" - }, - "privateDnsZoneGroup": { - "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2024-05-01", - "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", - "properties": { - "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" - } - } - }, - "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" - ] + "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." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the SRV record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "srvRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of SRV records in the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "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')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "SRV": { + "type": "Microsoft.Network/privateDnsZones/SRV", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "properties": { + "metadata": "[parameters('metadata')]", + "srvRecords": "[parameters('srvRecords')]", + "ttl": "[parameters('ttl')]" + } + }, + "SRV_roleAssignments": { + "copy": { + "name": "SRV_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/SRV/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/SRV', parameters('privateDnsZoneName'), 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": [ + "SRV" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed SRV record." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed SRV record." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/SRV', parameters('privateDnsZoneName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed SRV record." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "privateDnsZone_TXT": { + "copy": { + "name": "privateDnsZone_TXT", + "count": "[length(coalesce(parameters('txt'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-TXTRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('txt'), createArray())[copyIndex()].name]" + }, + "metadata": { + "value": "[tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'metadata')]" + }, + "txtRecords": { + "value": "[tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'txtRecords')]" + }, + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'ttl'), 3600)]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'roleAssignments')]" + } + }, + "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.32.4.45862", + "templateHash": "1855369119498044639" + }, + "name": "Private DNS Zone TXT record", + "description": "This module deploys a Private DNS Zone TXT record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "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." } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the TXT record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "txtRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of TXT records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "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')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "TXT": { + "type": "Microsoft.Network/privateDnsZones/TXT", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "properties": { + "metadata": "[parameters('metadata')]", + "ttl": "[parameters('ttl')]", + "txtRecords": "[parameters('txtRecords')]" + } + }, + "TXT_roleAssignments": { + "copy": { + "name": "TXT_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/TXT/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/TXT', parameters('privateDnsZoneName'), 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": [ + "TXT" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed TXT record." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed TXT record." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/TXT', parameters('privateDnsZoneName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed TXT record." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "privateDnsZone_virtualNetworkLinks": { + "copy": { + "name": "privateDnsZone_virtualNetworkLinks", + "count": "[length(coalesce(parameters('virtualNetworkLinks'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-VirtualNetworkLink-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'name'), format('{0}-vnetlink', last(split(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()].virtualNetworkResourceId, '/'))))]" + }, + "virtualNetworkResourceId": { + "value": "[coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()].virtualNetworkResourceId]" + }, + "location": { + "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'location'), 'global')]" + }, + "registrationEnabled": { + "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'registrationEnabled'), false())]" + }, + "tags": { + "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + }, + "resolutionPolicy": { + "value": "[tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'resolutionPolicy')]" + } + }, + "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.32.4.45862", + "templateHash": "15326596012552051215" + }, + "name": "Private DNS Zone Virtual Network Link", + "description": "This module deploys a Private DNS Zone Virtual Network Link.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "defaultValue": "[format('{0}-vnetlink', last(split(parameters('virtualNetworkResourceId'), '/')))]", + "metadata": { + "description": "Optional. The name of the virtual network link." + } + }, + "location": { + "type": "string", + "defaultValue": "global", + "metadata": { + "description": "Optional. The location of the PrivateDNSZone. Should be global." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "registrationEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Is auto-registration of virtual machine records in the virtual network in the Private DNS zone enabled?." + } + }, + "virtualNetworkResourceId": { + "type": "string", + "metadata": { + "description": "Required. Link to another virtual network resource ID." + } + }, + "resolutionPolicy": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resolution policy on the virtual network link. Only applicable for virtual network links to privatelink zones, and for A,AAAA,CNAME queries. When set to `NxDomainRedirect`, Azure DNS resolver falls back to public resolution if private dns query resolution results in non-existent domain response. `Default` is configured as the default option." + } + } + }, + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "virtualNetworkLink": { + "type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks", + "apiVersion": "2024-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "registrationEnabled": "[parameters('registrationEnabled')]", + "virtualNetwork": { + "id": "[parameters('virtualNetworkResourceId')]" + }, + "resolutionPolicy": "[parameters('resolutionPolicy')]" + } + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed virtual network link." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed virtual network link." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/virtualNetworkLinks', parameters('privateDnsZoneName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed virtual network link." + }, + "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('virtualNetworkLink', '2024-06-01', 'full').location]" + } + } + } + }, + "dependsOn": [ + "privateDnsZone" + ] + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the private DNS zone was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the private DNS zone." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the private DNS zone." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones', parameters('name'))]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('privateDnsZone', '2020-06-01', 'full').location]" + } + } + } + } + }, + "aiSearch": { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('{0}-search-services-deployment', variables('nameFormatted')), 64)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[variables('nameFormatted')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "cmkEnforcement": { + "value": "Disabled" + }, + "managedIdentities": { + "value": { + "systemAssigned": true + } + }, + "publicNetworkAccess": "[if(parameters('networkIsolation'), createObject('value', 'Disabled'), createObject('value', 'Enabled'))]", + "networkRuleSet": { + "value": { + "bypass": "AzureServices" + } + }, + "disableLocalAuth": { + "value": true + }, + "sku": { + "value": "standard" + }, + "partitionCount": { + "value": 1 + }, + "replicaCount": { + "value": 3 + }, + "roleAssignments": { + "value": "[parameters('roleAssignments')]" + }, + "diagnosticSettings": { + "value": [ + { + "workspaceResourceId": "[parameters('logAnalyticsWorkspaceResourceId')]" + } + ] + }, + "privateEndpoints": "[if(parameters('networkIsolation'), createObject('value', createArray(createObject('privateDnsZoneGroup', createObject('privateDnsZoneGroupConfigs', createArray(createObject('privateDnsZoneResourceId', reference('privateDnsZone').outputs.resourceId.value))), 'subnetResourceId', parameters('virtualNetworkSubnetResourceId')))), createObject('value', createArray()))]", + "tags": { + "value": "[parameters('tags')]" + } + }, + "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.34.44.8038", + "templateHash": "3202610734707871278" + }, + "name": "Search Services", + "description": "This module deploys a Search Service." + }, + "definitions": { + "secretsExportConfigurationType": { + "type": "object", + "properties": { + "keyVaultResourceId": { + "type": "string", + "metadata": { + "description": "Required. The key vault name where to store the API Admin keys generated by the modules." + } + }, + "primaryAdminKeyName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The primaryAdminKey secret name to create." + } + }, + "secondaryAdminKeyName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The secondaryAdminKey secret name to create." + } + } + } + }, + "secretsOutputType": { + "type": "object", + "properties": {}, + "additionalProperties": { + "$ref": "#/definitions/secretSetType", + "metadata": { + "description": "An exported secret's references." + } + } + }, + "authOptionsType": { + "type": "object", + "properties": { + "aadOrApiKey": { + "type": "object", + "properties": { + "aadAuthFailureMode": { + "type": "string", + "allowedValues": [ + "http401WithBearerChallenge", + "http403" + ], + "nullable": true, + "metadata": { + "description": "Optional. Describes what response the data plane API of a search service would send for requests that failed authentication." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. Indicates that either the API key or an access token from a Microsoft Entra ID tenant can be used for authentication." + } + }, + "apiKeyOnly": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Indicates that only the API key can be used for authentication." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "networkRuleSetType": { + "type": "object", + "properties": { + "bypass": { + "type": "string", + "allowedValues": [ + "AzurePortal", + "AzureServices", + "None" + ], + "nullable": true, + "metadata": { + "description": "Optional. Network specific rules that determine how the Azure AI Search service may be reached." + } + }, + "ipRules": { + "type": "array", + "items": { + "$ref": "#/definitions/ipRuleType" + }, + "nullable": true, + "metadata": { + "description": "Optional. A list of IP restriction rules that defines the inbound network(s) with allowing access to the search service endpoint. At the meantime, all other public IP networks are blocked by the firewall. These restriction rules are applied only when the 'publicNetworkAccess' of the search service is 'enabled'; otherwise, traffic over public interface is not allowed even with any public IP rules, and private endpoint connections would be the exclusive access method." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "ipRuleType": { + "type": "object", + "properties": { + "value": { + "type": "string", + "metadata": { + "description": "Required. Value corresponding to a single IPv4 address (eg., 123.1.2.3) or an IP range in CIDR format (eg., 123.1.2.3/24) to be allowed." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "_1.privateEndpointCustomDnsConfigType": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. FQDN that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "_1.privateEndpointIpConfigurationType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource that is unique within a resource group." + } + }, + "properties": { + "type": "object", + "properties": { + "groupId": { + "type": "string", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } + } + }, + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "_1.privateEndpointPrivateDnsZoneGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "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": "Required. The private DNS Zone Groups to associate the Private Endpoint. A DNS Zone Group can support up to 5 DNS zones." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "diagnosticSettingFullType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "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 logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "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 both logs & metrics are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "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." + } + } + }, + "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.5.1" + } + } + }, + "managedIdentityAllType": { + "type": "object", + "properties": { + "systemAssigned": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enables system assigned managed identity on the resource." + } + }, + "userAssignedResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The resource ID(s) to assign to the resource. Required if a user assigned identity is used for encryption." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a managed identity configuration. To be used if both a system-assigned & user-assigned identities are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "privateEndpointSingleServiceType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private Endpoint." + } + }, + "location": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The location to deploy the Private Endpoint to." + } + }, + "privateLinkServiceConnectionName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private link connection to create." + } + }, + "service": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The subresource to deploy the Private Endpoint for. For example \"vault\" for a Key Vault Private Endpoint." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + } + }, + "resourceGroupResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the Resource Group the Private Endpoint will be created in. If not specified, the Resource Group of the provided Virtual Network Subnet is used." + } + }, + "privateDnsZoneGroup": { + "$ref": "#/definitions/_1.privateEndpointPrivateDnsZoneGroupType", + "nullable": true, + "metadata": { + "description": "Optional. The private DNS Zone Group to configure for the Private Endpoint." + } + }, + "isManualConnection": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. If Manual Private Link Connection is required." + } + }, + "manualConnectionRequestMessage": { + "type": "string", + "nullable": true, + "maxLength": 140, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with the manual connection request." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/_1.privateEndpointCustomDnsConfigType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Custom DNS configurations." + } + }, + "ipConfigurations": { + "type": "array", + "items": { + "$ref": "#/definitions/_1.privateEndpointIpConfigurationType" + }, + "nullable": true, + "metadata": { + "description": "Optional. A list of IP configurations of the Private Endpoint. This will be used to map to the first-party Service endpoints." + } + }, + "applicationSecurityGroupResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Application security groups in which the Private Endpoint IP configuration is included." + } + }, + "customNetworkInterfaceName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The custom name of the network interface attached to the Private Endpoint." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags to be applied on all resources/Resource Groups in this deployment." + } + }, + "enableTelemetry": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a private endpoint. To be used if the private endpoint's default service / groupId can be assumed (i.e., for services that only have one Private Endpoint type like 'vault' for key vault).", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "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.5.1" + } + } + }, + "secretSetType": { + "type": "object", + "properties": { + "secretResourceId": { + "type": "string", + "metadata": { + "description": "The resourceId of the exported secret." + } + }, + "secretUri": { + "type": "string", + "metadata": { + "description": "The secret URI of the exported secret." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "modules/keyVaultExport.bicep" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the Azure Cognitive Search service to create or update. Search service names must only contain lowercase letters, digits or dashes, cannot use dash as the first two or last one characters, cannot contain consecutive dashes, and must be between 2 and 60 characters in length. Search service names must be globally unique since they are part of the service URI (https://.search.windows.net). You cannot change the service name after the service is created." + } + }, + "authOptions": { + "$ref": "#/definitions/authOptionsType", + "nullable": true, + "metadata": { + "description": "Optional. Defines the options for how the data plane API of a Search service authenticates requests. Must remain an empty object {} if 'disableLocalAuth' is set to true." + } + }, + "disableLocalAuth": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. When set to true, calls to the search service will not be permitted to utilize API keys for authentication. This cannot be set to true if 'authOptions' are defined." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "cmkEnforcement": { + "type": "string", + "defaultValue": "Unspecified", + "allowedValues": [ + "Disabled", + "Enabled", + "Unspecified" + ], + "metadata": { + "description": "Optional. Describes a policy that determines how resources within the search service are to be encrypted with Customer Managed Keys." + } + }, + "hostingMode": { + "type": "string", + "defaultValue": "default", + "allowedValues": [ + "default", + "highDensity" + ], + "metadata": { + "description": "Optional. Applicable only for the standard3 SKU. You can set this property to enable up to 3 high density partitions that allow up to 1000 indexes, which is much higher than the maximum indexes allowed for any other SKU. For the standard3 SKU, the value is either 'default' or 'highDensity'. For all other SKUs, this value must be 'default'." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all Resources." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "nullable": true, + "metadata": { + "description": "Optional. The lock settings for all Resources in the solution." + } + }, + "networkRuleSet": { + "$ref": "#/definitions/networkRuleSetType", + "nullable": true, + "metadata": { + "description": "Optional. Network specific rules that determine how the Azure Cognitive Search service may be reached." + } + }, + "partitionCount": { + "type": "int", + "defaultValue": 1, + "minValue": 1, + "maxValue": 12, + "metadata": { + "description": "Optional. The number of partitions in the search service; if specified, it can be 1, 2, 3, 4, 6, or 12. Values greater than 1 are only valid for standard SKUs. For 'standard3' services with hostingMode set to 'highDensity', the allowed values are between 1 and 3." + } + }, + "privateEndpoints": { + "type": "array", + "items": { + "$ref": "#/definitions/privateEndpointSingleServiceType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible." + } + }, + "sharedPrivateLinkResources": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The sharedPrivateLinkResources to create as part of the search Service." + } + }, + "publicNetworkAccess": { + "type": "string", + "defaultValue": "Enabled", + "allowedValues": [ + "Enabled", + "Disabled" + ], + "metadata": { + "description": "Optional. This value can be set to 'Enabled' to avoid breaking changes on existing customer resources and templates. If set to 'Disabled', traffic over public interface is not allowed, and private endpoint connections would be the exclusive access method." + } + }, + "secretsExportConfiguration": { + "$ref": "#/definitions/secretsExportConfigurationType", + "nullable": true, + "metadata": { + "description": "Optional. Key vault reference and secret settings for the module's secrets export." + } + }, + "replicaCount": { + "type": "int", + "defaultValue": 3, + "minValue": 1, + "maxValue": 12, + "metadata": { + "description": "Optional. The number of replicas in the search service. If specified, it must be a value between 1 and 12 inclusive for standard SKUs or between 1 and 3 inclusive for basic SKU." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "semanticSearch": { + "type": "string", + "nullable": true, + "allowedValues": [ + "disabled", + "free", + "standard" + ], + "metadata": { + "description": "Optional. Sets options that control the availability of semantic search. This configuration is only possible for certain search SKUs in certain locations." + } + }, + "sku": { + "type": "string", + "defaultValue": "standard", + "allowedValues": [ + "basic", + "free", + "standard", + "standard2", + "standard3", + "storage_optimized_l1", + "storage_optimized_l2" + ], + "metadata": { + "description": "Optional. Defines the SKU of an Azure Cognitive Search Service, which determines price tier and capacity limits." + } + }, + "managedIdentities": { + "$ref": "#/definitions/managedIdentityAllType", + "nullable": true, + "metadata": { + "description": "Optional. The managed identity definition for this resource." + } + }, + "diagnosticSettings": { + "type": "array", + "items": { + "$ref": "#/definitions/diagnosticSettingFullType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags to help categorize the resource in the Azure portal." + } + } + }, + "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)))))]" + } + ], + "enableReferencedModulesTelemetry": false, + "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", + "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned,UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', '')), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Search Index Data Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8ebe5a00-799e-43f5-93ac-243d3dce84a7')]", + "Search Index Data Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1407120a-92aa-4202-b7e9-c0e197c71c8f')]", + "Search Service Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7ca78c08-252a-4471-8644-bb5ff32d4ba0')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.search-searchservice.{0}.{1}', replace('0.10.0', '.', '-'), 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" + } + } + } + } + }, + "searchService": { + "type": "Microsoft.Search/searchServices", + "apiVersion": "2025-02-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "sku": { + "name": "[parameters('sku')]" + }, + "tags": "[parameters('tags')]", + "identity": "[variables('identity')]", + "properties": { + "authOptions": "[parameters('authOptions')]", + "disableLocalAuth": "[parameters('disableLocalAuth')]", + "encryptionWithCmk": { + "enforcement": "[parameters('cmkEnforcement')]" + }, + "hostingMode": "[parameters('hostingMode')]", + "networkRuleSet": "[parameters('networkRuleSet')]", + "partitionCount": "[parameters('partitionCount')]", + "replicaCount": "[parameters('replicaCount')]", + "publicNetworkAccess": "[toLower(parameters('publicNetworkAccess'))]", + "semanticSearch": "[parameters('semanticSearch')]" + } + }, + "searchService_diagnosticSettings": { + "copy": { + "name": "searchService_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Search/searchServices/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null + } + }, + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "searchService" + ] + }, + "searchService_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.Search/searchServices/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "searchService" + ] + }, + "searchService_roleAssignments": { + "copy": { + "name": "searchService_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Search/searchServices/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Search/searchServices', 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": [ + "searchService" + ] + }, + "searchService_privateEndpoints": { + "copy": { + "name": "searchService_privateEndpoints", + "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-searchService-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "resourceGroup": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupName'), '')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'name'), format('pep-{0}-{1}-{2}', last(split(resourceId('Microsoft.Search/searchServices', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'searchService'), copyIndex()))]" + }, + "privateLinkServiceConnections": "[if(not(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true())), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.Search/searchServices', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'searchService'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.Search/searchServices', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'searchService')))))), createObject('value', null()))]", + "manualPrivateLinkServiceConnections": "[if(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true()), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.Search/searchServices', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'searchService'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.Search/searchServices', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'searchService')), 'requestMessage', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualConnectionRequestMessage'), 'Manual approval required.'))))), createObject('value', null()))]", + "subnetResourceId": { + "value": "[coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId]" + }, + "enableTelemetry": { + "value": "[variables('enableReferencedModulesTelemetry')]" + }, + "location": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'location'), reference(split(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location)]" + }, + "lock": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" + }, + "privateDnsZoneGroup": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroup')]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'roleAssignments')]" + }, + "tags": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + }, + "customDnsConfigs": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customDnsConfigs')]" + }, + "ipConfigurations": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'ipConfigurations')]" + }, + "applicationSecurityGroupResourceIds": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'applicationSecurityGroupResourceIds')]" + }, + "customNetworkInterfaceName": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customNetworkInterfaceName')]" + } + }, + "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.34.44.8038", + "templateHash": "12389807800450456797" + }, + "name": "Private Endpoints", + "description": "This module deploys a Private Endpoint." + }, + "definitions": { + "privateDnsZoneGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" }, - "outputs": { - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group the private endpoint was deployed into." - }, - "value": "[resourceGroup().name]" - }, - "resourceId": { + "metadata": { + "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "ipConfigurationType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource that is unique within a resource group." + } + }, + "properties": { + "type": "object", + "properties": { + "groupId": { "type": "string", "metadata": { - "description": "The resource ID of the private endpoint." - }, - "value": "[resourceId('Microsoft.Network/privateEndpoints', parameters('name'))]" + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." + } }, - "name": { + "memberName": { "type": "string", "metadata": { - "description": "The name of the private endpoint." - }, - "value": "[parameters('name')]" + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." + } }, - "location": { + "privateIPAddress": { "type": "string", "metadata": { - "description": "The location the resource was deployed into." - }, - "value": "[reference('privateEndpoint', '2024-05-01', 'full').location]" - }, - "customDnsConfigs": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } + } + }, + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "privateLinkServiceConnectionType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the private link service connection." + } + }, + "properties": { + "type": "object", + "properties": { + "groupIds": { "type": "array", "items": { - "$ref": "#/definitions/customDnsConfigType" + "type": "string" }, "metadata": { - "description": "The custom DNS configurations of the private endpoint." - }, - "value": "[reference('privateEndpoint').customDnsConfigs]" + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." + } }, - "networkInterfaceResourceIds": { - "type": "array", - "items": { - "type": "string" - }, + "privateLinkServiceId": { + "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))]" + "description": "Required. The resource id of private link service." + } }, - "groupId": { + "requestMessage": { "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))]" + "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." + } + } + }, + "metadata": { + "description": "Required. Properties of private link service connection." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "customDnsConfigType": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. FQDN that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "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." + } + } + }, + "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.5.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": { + "__bicep_imported_from!": { + "sourceTemplate": "private-dns-zone-group/main.bicep" + } + } + }, + "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.5.1" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the private endpoint resource to create." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + } + }, + "applicationSecurityGroupResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Application security groups in which the private endpoint IP configuration is included." + } + }, + "customNetworkInterfaceName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The custom name of the network interface attached to the private endpoint." + } + }, + "ipConfigurations": { + "type": "array", + "items": { + "$ref": "#/definitions/ipConfigurationType" + }, + "nullable": true, + "metadata": { + "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." + } + }, + "privateDnsZoneGroup": { + "$ref": "#/definitions/privateDnsZoneGroupType", + "nullable": true, + "metadata": { + "description": "Optional. The private DNS zone group to configure for the private endpoint." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "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", + "nullable": true, + "metadata": { + "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/customDnsConfigType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Custom DNS configurations." + } + }, + "manualPrivateLinkServiceConnections": { + "type": "array", + "items": { + "$ref": "#/definitions/privateLinkServiceConnectionType" + }, + "nullable": true, + "metadata": { + "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." + } + }, + "privateLinkServiceConnections": { + "type": "array", + "items": { + "$ref": "#/definitions/privateLinkServiceConnectionType" + }, + "nullable": true, + "metadata": { + "description": "Conditional. A grouping of information about the connection to the remote resource. Required if `manualPrivateLinkServiceConnections` is empty." + } + }, + "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": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.11.0', '.', '-'), 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-05-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": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" }, "dependsOn": [ - "cognitiveService" + "privateEndpoint" ] }, - "secretsExport": { - "condition": "[not(equals(parameters('secretsExportConfiguration'), null()))]", + "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": "2022-09-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]]", + "name": "[format('{0}-PrivateEndpoint-PrivateDnsZoneGroup', uniqueString(deployment().name))]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "keyVaultName": { - "value": "[last(split(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '/'))]" + "name": { + "value": "[tryGet(parameters('privateDnsZoneGroup'), 'name')]" }, - "secretsToSet": { - "value": "[union(createArray(), if(contains(parameters('secretsExportConfiguration'), 'accessKey1Name'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'accessKey1Name'), 'value', listKeys('cognitiveService', '2025-04-01-preview').key1)), createArray()), if(contains(parameters('secretsExportConfiguration'), 'accessKey2Name'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'accessKey2Name'), 'value', listKeys('cognitiveService', '2025-04-01-preview').key2)), createArray()))]" + "privateEndpointName": { + "value": "[parameters('name')]" + }, + "privateDnsZoneConfigs": { + "value": "[parameters('privateDnsZoneGroup').privateDnsZoneGroupConfigs]" } }, "template": { @@ -55434,313 +66012,613 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "1200612323329026557" - } + "version": "0.34.44.8038", + "templateHash": "13997305779829540948" + }, + "name": "Private Endpoint Private DNS Zone Groups", + "description": "This module deploys a Private Endpoint Private DNS Zone Group." }, "definitions": { - "secretSetOutputType": { - "type": "object", - "properties": { - "secretResourceId": { - "type": "string", - "metadata": { - "description": "The resourceId of the exported secret." - } - }, - "secretUri": { - "type": "string", - "metadata": { - "description": "The secret URI of the exported secret." - } - }, - "secretUriWithVersion": { - "type": "string", - "metadata": { - "description": "The secret URI with version of the exported secret." - } - } - }, - "metadata": { - "description": "An AVM-aligned type for the output of the secret set via the secrets export feature.", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } - } - }, - "secretToSetType": { + "privateDnsZoneGroupConfigType": { "type": "object", "properties": { "name": { "type": "string", + "nullable": true, "metadata": { - "description": "Required. The name of the secret to set." + "description": "Optional. The name of the private DNS zone group config." } }, - "value": { - "type": "securestring", + "privateDnsZoneResourceId": { + "type": "string", "metadata": { - "description": "Required. The value of the secret to set." + "description": "Required. The resource id of the private DNS zone." } } }, "metadata": { - "description": "An AVM-aligned type for the secret to set via the secrets export feature.", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } + "__bicep_export!": true } } }, "parameters": { - "keyVaultName": { + "privateEndpointName": { "type": "string", "metadata": { - "description": "Required. The name of the Key Vault to set the ecrets in." + "description": "Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment." } }, - "secretsToSet": { + "privateDnsZoneConfigs": { "type": "array", "items": { - "$ref": "#/definitions/secretToSetType" + "$ref": "#/definitions/privateDnsZoneGroupConfigType" }, + "minLength": 1, + "maxLength": 5, "metadata": { - "description": "Required. The secrets to set in the Key Vault." + "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." } } }, + "variables": { + "copy": [ + { + "name": "privateDnsZoneConfigsVar", + "count": "[length(parameters('privateDnsZoneConfigs'))]", + "input": { + "name": "[coalesce(tryGet(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')], 'name'), last(split(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId, '/')))]", + "properties": { + "privateDnsZoneId": "[parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId]" + } + } + } + ] + }, "resources": { - "keyVault": { + "privateEndpoint": { "existing": true, - "type": "Microsoft.KeyVault/vaults", - "apiVersion": "2023-07-01", - "name": "[parameters('keyVaultName')]" + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2024-05-01", + "name": "[parameters('privateEndpointName')]" }, - "secrets": { - "copy": { - "name": "secrets", - "count": "[length(parameters('secretsToSet'))]" - }, - "type": "Microsoft.KeyVault/vaults/secrets", - "apiVersion": "2023-07-01", - "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('secretsToSet')[copyIndex()].name)]", + "privateDnsZoneGroup": { + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2024-05-01", + "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", "properties": { - "value": "[parameters('secretsToSet')[copyIndex()].value]" + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" } } }, "outputs": { - "secretsSet": { - "type": "array", - "items": { - "$ref": "#/definitions/secretSetOutputType" + "name": { + "type": "string", + "metadata": { + "description": "The name of the private endpoint DNS zone group." }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", "metadata": { - "description": "The references to the secrets exported to the provided Key Vault." + "description": "The resource ID of the private endpoint DNS zone group." }, - "copy": { - "count": "[length(range(0, length(coalesce(parameters('secretsToSet'), createArray()))))]", - "input": { - "secretResourceId": "[resourceId('Microsoft.KeyVault/vaults/secrets', parameters('keyVaultName'), parameters('secretsToSet')[range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()]].name)]", - "secretUri": "[reference(format('secrets[{0}]', range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()])).secretUri]", - "secretUriWithVersion": "[reference(format('secrets[{0}]', range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()])).secretUriWithVersion]" - } - } + "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": [ - "cognitiveService" - ] + }, + "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-05-01', 'full').location]" + }, + "customDnsConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/customDnsConfigType" + }, + "metadata": { + "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": [ + "searchService" + ] + }, + "searchService_sharedPrivateLinkResources": { + "copy": { + "name": "searchService_sharedPrivateLinkResources", + "count": "[length(parameters('sharedPrivateLinkResources'))]", + "mode": "serial", + "batchSize": 1 + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-searchService-SharedPrvLink-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(tryGet(parameters('sharedPrivateLinkResources')[copyIndex()], 'name'), format('spl-{0}-{1}-{2}', last(split(resourceId('Microsoft.Search/searchServices', parameters('name')), '/')), parameters('sharedPrivateLinkResources')[copyIndex()].groupId, copyIndex()))]" + }, + "searchServiceName": { + "value": "[parameters('name')]" + }, + "privateLinkResourceId": { + "value": "[parameters('sharedPrivateLinkResources')[copyIndex()].privateLinkResourceId]" + }, + "groupId": { + "value": "[parameters('sharedPrivateLinkResources')[copyIndex()].groupId]" + }, + "requestMessage": { + "value": "[parameters('sharedPrivateLinkResources')[copyIndex()].requestMessage]" + }, + "resourceRegion": { + "value": "[tryGet(parameters('sharedPrivateLinkResources')[copyIndex()], 'resourceRegion')]" + } + }, + "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.34.44.8038", + "templateHash": "17921821442902726660" + }, + "name": "Search Services Private Link Resources", + "description": "This module deploys a Search Service Private Link Resource." + }, + "parameters": { + "searchServiceName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent searchServices. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the shared private link resource managed by the Azure Cognitive Search service within the specified resource group." + } + }, + "privateLinkResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the resource the shared private link resource is for." + } + }, + "groupId": { + "type": "string", + "metadata": { + "description": "Required. The group ID from the provider of resource the shared private link resource is for." + } + }, + "requestMessage": { + "type": "string", + "metadata": { + "description": "Required. The request message for requesting approval of the shared private link resource." + } + }, + "resourceRegion": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Can be used to specify the Azure Resource Manager location of the resource to which a shared private link is to be created. This is only required for those resources whose DNS configuration are regional (such as Azure Kubernetes Service)." + } + } + }, + "resources": { + "searchService": { + "existing": true, + "type": "Microsoft.Search/searchServices", + "apiVersion": "2023-11-01", + "name": "[parameters('searchServiceName')]" + }, + "sharedPrivateLinkResource": { + "type": "Microsoft.Search/searchServices/sharedPrivateLinkResources", + "apiVersion": "2025-02-01-preview", + "name": "[format('{0}/{1}', parameters('searchServiceName'), parameters('name'))]", + "properties": { + "privateLinkResourceId": "[parameters('privateLinkResourceId')]", + "groupId": "[parameters('groupId')]", + "requestMessage": "[parameters('requestMessage')]", + "resourceRegion": "[parameters('resourceRegion')]" + } + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the shared private link resource." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the shared private link resource." + }, + "value": "[resourceId('Microsoft.Search/searchServices/sharedPrivateLinkResources', parameters('searchServiceName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the shared private link resource was created in." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "searchService" + ] + }, + "secretsExport": { + "condition": "[not(equals(parameters('secretsExportConfiguration'), null()))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-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]]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "keyVaultName": { + "value": "[last(split(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '/'))]" + }, + "secretsToSet": { + "value": "[union(createArray(), if(contains(parameters('secretsExportConfiguration'), 'primaryAdminKeyName'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'primaryAdminKeyName'), 'value', listAdminKeys(resourceId('Microsoft.Search/searchServices', parameters('name')), '2025-02-01-preview').primaryKey)), createArray()), if(contains(parameters('secretsExportConfiguration'), 'secondaryAdminKeyName'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'secondaryAdminKeyName'), 'value', listAdminKeys(resourceId('Microsoft.Search/searchServices', parameters('name')), '2025-02-01-preview').secondaryKey)), createArray()))]" + } + }, + "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.34.44.8038", + "templateHash": "8727860379251085593" + } + }, + "definitions": { + "secretSetType": { + "type": "object", + "properties": { + "secretResourceId": { + "type": "string", + "metadata": { + "description": "The resourceId of the exported secret." + } + }, + "secretUri": { + "type": "string", + "metadata": { + "description": "The secret URI of the exported secret." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "secretToSetType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the secret to set." + } + }, + "value": { + "type": "securestring", + "metadata": { + "description": "Required. The value of the secret to set." + } + } + } } }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the cognitive services account." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the cognitive services account." - }, - "value": "[resourceId('Microsoft.CognitiveServices/accounts', parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group the cognitive services account was deployed into." - }, - "value": "[resourceGroup().name]" - }, - "endpoint": { + "parameters": { + "keyVaultName": { "type": "string", "metadata": { - "description": "The service endpoint of the cognitive services account." - }, - "value": "[reference('cognitiveService').endpoint]" - }, - "endpoints": { - "$ref": "#/definitions/endpointType", - "metadata": { - "description": "All endpoints available for the cognitive services account, types depends on the cognitive service kind." - }, - "value": "[reference('cognitiveService').endpoints]" + "description": "Required. The name of the Key Vault to set the ecrets in." + } }, - "systemAssignedMIPrincipalId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "The principal ID of the system assigned identity." + "secretsToSet": { + "type": "array", + "items": { + "$ref": "#/definitions/secretToSetType" }, - "value": "[tryGet(tryGet(reference('cognitiveService', '2025-04-01-preview', 'full'), 'identity'), 'principalId')]" - }, - "location": { - "type": "string", "metadata": { - "description": "The location the resource was deployed into." - }, - "value": "[reference('cognitiveService', '2025-04-01-preview', 'full').location]" + "description": "Required. The secrets to set in the Key Vault." + } + } + }, + "resources": { + "keyVault": { + "existing": true, + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2022-07-01", + "name": "[parameters('keyVaultName')]" }, - "exportedSecrets": { - "$ref": "#/definitions/secretsOutputType", - "metadata": { - "description": "A hashtable of references to the secrets exported to the provided Key Vault. The key of each reference is each secret's name." + "secrets": { + "copy": { + "name": "secrets", + "count": "[length(parameters('secretsToSet'))]" }, - "value": "[if(not(equals(parameters('secretsExportConfiguration'), null())), toObject(reference('secretsExport').outputs.secretsSet.value, lambda('secret', last(split(lambdaVariables('secret').secretResourceId, '/'))), lambda('secret', lambdaVariables('secret'))), createObject())]" - }, - "privateEndpoints": { + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2023-07-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('secretsToSet')[copyIndex()].name)]", + "properties": { + "value": "[parameters('secretsToSet')[copyIndex()].value]" + } + } + }, + "outputs": { + "secretsSet": { "type": "array", "items": { - "$ref": "#/definitions/privateEndpointOutputType" + "$ref": "#/definitions/secretSetType" }, "metadata": { - "description": "The private endpoints of the congitive services account." + "description": "The references to the secrets exported to the provided Key Vault." }, "copy": { - "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]", + "count": "[length(range(0, length(coalesce(parameters('secretsToSet'), createArray()))))]", "input": { - "name": "[reference(format('cognitiveService_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", - "resourceId": "[reference(format('cognitiveService_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", - "groupId": "[tryGet(tryGet(reference(format('cognitiveService_privateEndpoints[{0}]', copyIndex())).outputs, 'groupId'), 'value')]", - "customDnsConfigs": "[reference(format('cognitiveService_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfigs.value]", - "networkInterfaceResourceIds": "[reference(format('cognitiveService_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceResourceIds.value]" + "secretResourceId": "[resourceId('Microsoft.KeyVault/vaults/secrets', parameters('keyVaultName'), parameters('secretsToSet')[range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()]].name)]", + "secretUri": "[reference(format('secrets[{0}]', range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()])).secretUri]" } } } } } - } + }, + "dependsOn": [ + "searchService" + ] } }, "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the search service." + }, + "value": "[parameters('name')]" + }, "resourceId": { "type": "string", - "value": "[reference('cognitiveService').outputs.resourceId.value]" + "metadata": { + "description": "The resource ID of the search service." + }, + "value": "[resourceId('Microsoft.Search/searchServices', parameters('name'))]" }, - "name": { + "resourceGroupName": { "type": "string", - "value": "[reference('cognitiveService').outputs.name.value]" + "metadata": { + "description": "The name of the resource group the search service was created in." + }, + "value": "[resourceGroup().name]" }, "systemAssignedMIPrincipalId": { "type": "string", "nullable": true, - "value": "[tryGet(tryGet(reference('cognitiveService').outputs, 'systemAssignedMIPrincipalId'), 'value')]" + "metadata": { + "description": "The principal ID of the system assigned identity." + }, + "value": "[tryGet(tryGet(reference('searchService', '2025-02-01-preview', 'full'), 'identity'), 'principalId')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('searchService', '2025-02-01-preview', 'full').location]" }, "endpoint": { "type": "string", - "value": "[reference('cognitiveService').outputs.endpoint.value]" + "metadata": { + "description": "The endpoint of the search service." + }, + "value": "[reference('searchService').endpoint]" }, - "foundryConnection": { - "type": "object", - "value": { - "name": "[reference('cognitiveService').outputs.name.value]", - "value": null, - "category": "[parameters('category')]", - "target": "[reference('cognitiveService').outputs.endpoint.value]", - "kind": "[parameters('kind')]", - "connectionProperties": { - "authType": "AAD" - }, - "isSharedToAll": true, - "metadata": { - "ApiType": "Azure", - "Kind": "[parameters('kind')]", - "ResourceId": "[reference('cognitiveService').outputs.resourceId.value]" - } - } + "exportedSecrets": { + "$ref": "#/definitions/secretsOutputType", + "metadata": { + "description": "A hashtable of references to the secrets exported to the provided Key Vault. The key of each reference is each secret's name." + }, + "value": "[if(not(equals(parameters('secretsExportConfiguration'), null())), toObject(reference('secretsExport').outputs.secretsSet.value, lambda('secret', last(split(lambdaVariables('secret').secretResourceId, '/'))), lambda('secret', lambdaVariables('secret'))), createObject())]" } } } }, "dependsOn": [ - "cognitiveServicesPrivateDnsZone" + "privateDnsZone" ] } }, "outputs": { - "aiServicesResourceId": { - "type": "string", - "value": "[reference('aiServices').outputs.resourceId.value]" - }, - "aiServicesName": { + "resourceId": { "type": "string", - "value": "[reference('aiServices').outputs.name.value]" + "value": "[reference('aiSearch').outputs.resourceId.value]" }, - "aiServicesEndpoint": { + "name": { "type": "string", - "value": "[reference('aiServices').outputs.endpoint.value]" + "value": "[reference('aiSearch').outputs.name.value]" }, - "aiServicesSystemAssignedMIPrincipalId": { + "systemAssignedMIPrincipalId": { "type": "string", - "value": "[coalesce(tryGet(tryGet(reference('aiServices').outputs, 'systemAssignedMIPrincipalId'), 'value'), '')]" - }, - "connections": { - "type": "array", - "value": "[union(createArray(reference('aiServices').outputs.foundryConnection.value), if(parameters('contentSafetyEnabled'), createArray(reference('contentSafety').outputs.foundryConnection.value), createArray()), if(parameters('visionEnabled'), createArray(reference('vision').outputs.foundryConnection.value), createArray()), if(parameters('languageEnabled'), createArray(reference('language').outputs.foundryConnection.value), createArray()), if(parameters('speechEnabled'), createArray(reference('speech').outputs.foundryConnection.value), createArray()), if(parameters('translatorEnabled'), createArray(reference('translator').outputs.foundryConnection.value), createArray()), if(parameters('documentIntelligenceEnabled'), createArray(reference('documentIntelligence').outputs.foundryConnection.value), createArray()))]" + "value": "[coalesce(tryGet(tryGet(reference('aiSearch').outputs, 'systemAssignedMIPrincipalId'), 'value'), '')]" } } } }, "dependsOn": [ + "cognitiveServices", "logAnalyticsWorkspace", "network" ] }, - "project": { + "virtualMachine": { + "condition": "[parameters('networkIsolation')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}prj', parameters('name'))]", + "name": "[take(format('{0}-virtual-machine-deployment', parameters('name')), 64)]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "cosmosDBname": "[if(parameters('cosmosDbEnabled'), createObject('value', reference('cosmosDb').outputs.cosmosDBname.value), createObject('value', ''))]", - "cosmosDbEnabled": { - "value": "[parameters('cosmosDbEnabled')]" + "vmName": { + "value": "[toLower(format('vm-{0}-jump', parameters('name')))]" }, - "searchEnabled": { - "value": "[parameters('searchEnabled')]" + "vmNicName": { + "value": "[toLower(format('nic-vm-{0}-jump', parameters('name')))]" }, - "name": { - "value": "[parameters('projectName')]" + "vmSize": { + "value": "[parameters('vmSize')]" }, - "location": { - "value": "[parameters('aiDeploymentsLocation')]" + "vmSubnetId": { + "value": "[reference('network').outputs.defaultSubnetResourceId.value]" }, - "storageName": { + "storageAccountName": { "value": "[reference('storageAccount').outputs.storageName.value]" }, - "aiServicesName": { - "value": "[reference('cognitiveServices').outputs.aiServicesName.value]" + "storageAccountResourceGroup": { + "value": "[resourceGroup().name]" }, - "nameFormatted": "[if(parameters('searchEnabled'), createObject('value', reference('aiSearch').outputs.name.value), createObject('value', ''))]" + "imagePublisher": { + "value": "MicrosoftWindowsDesktop" + }, + "imageOffer": { + "value": "Windows-11" + }, + "imageSku": { + "value": "win11-23h2-ent" + }, + "authenticationType": { + "value": "password" + }, + "vmAdminUsername": { + "value": "[variables('servicesUsername')]" + }, + "vmAdminPasswordOrKey": { + "value": "[parameters('vmAdminPasswordOrKey')]" + }, + "diskStorageAccountType": { + "value": "Premium_LRS" + }, + "numDataDisks": { + "value": 1 + }, + "osDiskSize": { + "value": 128 + }, + "dataDiskSize": { + "value": 50 + }, + "dataDiskCaching": { + "value": "ReadWrite" + }, + "enableAcceleratedNetworking": { + "value": true + }, + "enableMicrosoftEntraIdAuth": { + "value": true + }, + "userObjectId": { + "value": "[parameters('userObjectId')]" + }, + "workspaceId": { + "value": "[reference('logAnalyticsWorkspace').outputs.resourceId.value]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('allTags')]" + } }, "template": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", @@ -55749,174 +66627,542 @@ "_generator": { "name": "bicep", "version": "0.36.1.42791", - "templateHash": "15578119539506362308" + "templateHash": "18206245333790613128" } }, "parameters": { - "name": { + "vmName": { "type": "string", - "minLength": 3, - "maxLength": 12, + "defaultValue": "TestVm", "metadata": { - "description": "The name of the environment. Use alphanumeric characters only." + "description": "Specifies the name of the virtual machine." } }, - "location": { + "vmSize": { "type": "string", + "defaultValue": "Standard_DS4_v2", "metadata": { - "description": "Specifies the location for all the Azure resources. Defaults to the location of the resource group." + "description": "Specifies the size of the virtual machine." } }, - "cosmosDBname": { + "vmSubnetId": { "type": "string", "metadata": { - "description": "Name of the customers existing CosmosDB Resource" + "description": "Specifies the resource id of the subnet hosting the virtual machine." } }, - "cosmosDbEnabled": { - "type": "bool", + "storageAccountName": { + "type": "string", "metadata": { - "description": "Whether to include Cosmos DB in the deployment." + "description": "Specifies the name of the storage account where the bootstrap diagnostic logs of the virtual machine are stored." } }, - "storageName": { + "storageAccountResourceGroup": { "type": "string", "metadata": { - "description": "Name of the customers existing Azure Storage Account" + "description": "Specifies the resource group of the storage account where the bootstrap diagnostic logs of the virtual machine are stored." } }, - "aiServicesName": { + "imagePublisher": { "type": "string", + "defaultValue": "MicrosoftWindowsServer", "metadata": { - "description": "Foundry Account Name" + "description": "Specifies the image publisher of the disk image used to create the virtual machine." } }, - "searchEnabled": { + "imageOffer": { + "type": "string", + "defaultValue": "WindowsServer", + "metadata": { + "description": "Specifies the offer of the platform image or marketplace image used to create the virtual machine." + } + }, + "imageSku": { + "type": "string", + "defaultValue": "2022-datacenter-azure-edition", + "metadata": { + "description": "Specifies the image version for the virtual machine." + } + }, + "authenticationType": { + "type": "string", + "defaultValue": "password", + "allowedValues": [ + "sshPublicKey", + "password" + ], + "metadata": { + "description": "Specifies the type of authentication when accessing the Virtual Machine. SSH key is recommended." + } + }, + "vmAdminUsername": { + "type": "string", + "metadata": { + "description": "Specifies the name of the administrator account of the virtual machine." + } + }, + "vmAdminPasswordOrKey": { + "type": "securestring", + "metadata": { + "description": "Specifies the SSH Key or password for the virtual machine. SSH key is recommended." + } + }, + "diskStorageAccountType": { + "type": "string", + "defaultValue": "Premium_LRS", + "allowedValues": [ + "Premium_LRS", + "StandardSSD_LRS", + "Standard_LRS", + "UltraSSD_LRS" + ], + "metadata": { + "description": "Specifies the storage account type for OS and data disk." + } + }, + "numDataDisks": { + "type": "int", + "defaultValue": 1, + "minValue": 0, + "maxValue": 64, + "metadata": { + "description": "Specifies the number of data disks of the virtual machine." + } + }, + "osDiskSize": { + "type": "int", + "defaultValue": 128, + "metadata": { + "description": "Specifies the size in GB of the OS disk of the VM." + } + }, + "dataDiskSize": { + "type": "int", + "defaultValue": 50, + "metadata": { + "description": "Specifies the size in GB of the OS disk of the virtual machine." + } + }, + "dataDiskCaching": { + "type": "string", + "defaultValue": "ReadWrite", + "metadata": { + "description": "Specifies the caching requirements for the data disks." + } + }, + "enableMicrosoftEntraIdAuth": { "type": "bool", + "defaultValue": true, "metadata": { - "description": "Whether to include Azure AI Search in the deployment." + "description": "Specifies whether enabling Microsoft Entra ID authentication on the virtual machine." } }, - "nameFormatted": { + "enableAcceleratedNetworking": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Specifies whether enabling accelerated networking on the virtual machine." + } + }, + "vmNicName": { "type": "string", "metadata": { - "description": "Azure Search Service Name" + "description": "Specifies the name of the network interface of the virtual machine." } }, - "defaultProjectName": { + "userObjectId": { "type": "string", - "defaultValue": "[parameters('name')]", + "defaultValue": "", "metadata": { - "description": "Name of the first project" + "description": "Specifies the object id of a Microsoft Entra ID user. In general, this the object id of the system administrator who deploys the Azure resources." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Specifies the location." + } + }, + "workspaceId": { + "type": "string", + "metadata": { + "description": "Specifies the resource id of the Log Analytics workspace." + } + }, + "tags": { + "type": "object", + "metadata": { + "description": "Specifies the resource tags." + } + } + }, + "variables": { + "randomString": "[uniqueString(resourceGroup().id, parameters('vmName'), parameters('vmAdminPasswordOrKey'))]", + "adminPassword": "[if(less(length(parameters('vmAdminPasswordOrKey')), 8), format('{0}{1}', parameters('vmAdminPasswordOrKey'), take(variables('randomString'), 12)), parameters('vmAdminPasswordOrKey'))]", + "linuxConfiguration": { + "disablePasswordAuthentication": true, + "ssh": { + "publicKeys": [ + { + "path": "[format('/home/{0}/.ssh/authorized_keys', parameters('vmAdminUsername'))]", + "keyData": "[variables('adminPassword')]" + } + ] + }, + "provisionVMAgent": true + } + }, + "resources": [ + { + "type": "Microsoft.Network/networkInterfaces", + "apiVersion": "2021-08-01", + "name": "[parameters('vmNicName')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "enableAcceleratedNetworking": "[parameters('enableAcceleratedNetworking')]", + "ipConfigurations": [ + { + "name": "ipconfig1", + "properties": { + "privateIPAllocationMethod": "Dynamic", + "subnet": { + "id": "[parameters('vmSubnetId')]" + } + } + } + ] } }, - "defaultProjectDisplayName": { - "type": "string", - "defaultValue": "[parameters('name')]" + { + "type": "Microsoft.Compute/virtualMachines", + "apiVersion": "2021-11-01", + "name": "[parameters('vmName')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "identity": { + "type": "SystemAssigned" + }, + "properties": { + "hardwareProfile": { + "vmSize": "[parameters('vmSize')]" + }, + "osProfile": { + "computerName": "[take(parameters('vmName'), 15)]", + "adminUsername": "[parameters('vmAdminUsername')]", + "adminPassword": "[variables('adminPassword')]", + "linuxConfiguration": "[if(equals(parameters('authenticationType'), 'password'), null(), variables('linuxConfiguration'))]" + }, + "storageProfile": { + "copy": [ + { + "name": "dataDisks", + "count": "[length(range(0, parameters('numDataDisks')))]", + "input": { + "caching": "[parameters('dataDiskCaching')]", + "diskSizeGB": "[parameters('dataDiskSize')]", + "lun": "[range(0, parameters('numDataDisks'))[copyIndex('dataDisks')]]", + "name": "[format('{0}-DataDisk{1}', parameters('vmName'), range(0, parameters('numDataDisks'))[copyIndex('dataDisks')])]", + "createOption": "Empty", + "managedDisk": { + "storageAccountType": "[parameters('diskStorageAccountType')]" + } + } + } + ], + "imageReference": { + "publisher": "[parameters('imagePublisher')]", + "offer": "[parameters('imageOffer')]", + "sku": "[parameters('imageSku')]", + "version": "latest" + }, + "osDisk": { + "name": "[format('{0}_OSDisk', parameters('vmName'))]", + "caching": "ReadWrite", + "createOption": "FromImage", + "diskSizeGB": "[parameters('osDiskSize')]", + "managedDisk": { + "storageAccountType": "[parameters('diskStorageAccountType')]" + } + } + }, + "networkProfile": { + "networkInterfaces": [ + { + "id": "[resourceId('Microsoft.Network/networkInterfaces', parameters('vmNicName'))]" + } + ] + }, + "diagnosticsProfile": { + "bootDiagnostics": { + "enabled": true, + "storageUri": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('storageAccountResourceGroup')), 'Microsoft.Storage/storageAccounts', parameters('storageAccountName')), '2021-09-01').primaryEndpoints.blob]" + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/networkInterfaces', parameters('vmNicName'))]" + ] + }, + { + "type": "Microsoft.Compute/virtualMachines/extensions", + "apiVersion": "2023-09-01", + "name": "[format('{0}/{1}', parameters('vmName'), 'DependencyAgentWindows')]", + "location": "[parameters('location')]", + "properties": { + "publisher": "Microsoft.Azure.Monitoring.DependencyAgent", + "type": "DependencyAgentWindows", + "typeHandlerVersion": "9.4", + "autoUpgradeMinorVersion": true, + "enableAutomaticUpgrade": true + }, + "dependsOn": [ + "[resourceId('Microsoft.Compute/virtualMachines', parameters('vmName'))]" + ] }, - "defaultProjectDescription": { - "type": "string", - "defaultValue": "This is a sample project for AI Foundry." - } - }, - "resources": [ { - "type": "Microsoft.CognitiveServices/accounts/projects", - "apiVersion": "2025-04-01-preview", - "name": "[format('{0}/{1}', parameters('aiServicesName'), parameters('defaultProjectName'))]", + "type": "Microsoft.Compute/virtualMachines/extensions", + "apiVersion": "2023-09-01", + "name": "[format('{0}/{1}', parameters('vmName'), 'AzureMonitorWindowsAgent')]", "location": "[parameters('location')]", - "identity": { - "type": "SystemAssigned" + "properties": { + "publisher": "Microsoft.Azure.Monitor", + "type": "AzureMonitorWindowsAgent", + "typeHandlerVersion": "1.0", + "autoUpgradeMinorVersion": true, + "enableAutomaticUpgrade": true }, + "dependsOn": [ + "[resourceId('Microsoft.Compute/virtualMachines/extensions', parameters('vmName'), 'DependencyAgentWindows')]", + "[resourceId('Microsoft.Compute/virtualMachines', parameters('vmName'))]" + ] + }, + { + "condition": "[parameters('enableMicrosoftEntraIdAuth')]", + "type": "Microsoft.Compute/virtualMachines/extensions", + "apiVersion": "2023-09-01", + "name": "[format('{0}/{1}', parameters('vmName'), 'AADLoginForWindows')]", + "location": "[parameters('location')]", "properties": { - "displayName": "[parameters('defaultProjectDisplayName')]", - "description": "[parameters('defaultProjectDescription')]" - } + "publisher": "Microsoft.Azure.ActiveDirectory", + "type": "AADLoginForWindows", + "typeHandlerVersion": "1.0", + "autoUpgradeMinorVersion": false, + "enableAutomaticUpgrade": false + }, + "dependsOn": [ + "[resourceId('Microsoft.Compute/virtualMachines/extensions', parameters('vmName'), 'AzureMonitorWindowsAgent')]", + "[resourceId('Microsoft.Compute/virtualMachines', parameters('vmName'))]" + ] }, { - "type": "Microsoft.CognitiveServices/accounts/projects/connections", - "apiVersion": "2025-04-01-preview", - "name": "[format('{0}/{1}/{2}', parameters('aiServicesName'), parameters('defaultProjectName'), parameters('storageName'))]", + "type": "Microsoft.Insights/dataCollectionRules", + "apiVersion": "2022-06-01", + "name": "DCR-Win-Event-Logs-to-LAW", + "location": "[parameters('location')]", + "kind": "Windows", "properties": { - "category": "AzureBlob", - "target": "[reference(resourceId('Microsoft.Storage/storageAccounts', parameters('storageName')), '2023-01-01').primaryEndpoints.blob]", - "authType": "AAD", - "metadata": { - "ApiType": "Azure", - "ResourceId": "[resourceId('Microsoft.Storage/storageAccounts', parameters('storageName'))]", - "location": "[reference(resourceId('Microsoft.Storage/storageAccounts', parameters('storageName')), '2023-01-01', 'full').location]", - "accountName": "[parameters('storageName')]", - "containerName": "[format('{0}proj', parameters('name'))]" + "dataFlows": [ + { + "destinations": [ + "logAnalytics" + ], + "streams": [ + "Microsoft-Event" + ] + } + ], + "dataSources": { + "windowsEventLogs": [ + { + "streams": [ + "Microsoft-Event" + ], + "xPathQueries": [ + "Application!*[System[(Level=1 or Level=2 or Level=3 or or Level=0) ]]", + "Security!*[System[(band(Keywords,13510798882111488))]]", + "System!*[System[(Level=1 or Level=2 or Level=3 or or Level=0)]]" + ], + "name": "eventLogsDataSource" + } + ] + }, + "description": "Collect Windows Event Logs and send to Azure Monitor Logs", + "destinations": { + "logAnalytics": [ + { + "name": "logAnalytics", + "workspaceResourceId": "[parameters('workspaceId')]" + } + ] } }, "dependsOn": [ - "[resourceId('Microsoft.CognitiveServices/accounts/projects', parameters('aiServicesName'), parameters('defaultProjectName'))]" + "[resourceId('Microsoft.Compute/virtualMachines/extensions', parameters('vmName'), 'AADLoginForWindows')]" ] }, { - "condition": "[parameters('searchEnabled')]", - "type": "Microsoft.CognitiveServices/accounts/projects/connections", - "apiVersion": "2025-04-01-preview", - "name": "[format('{0}/{1}/{2}', parameters('aiServicesName'), parameters('defaultProjectName'), if(parameters('searchEnabled'), parameters('nameFormatted'), ''))]", + "type": "Microsoft.Insights/dataCollectionRules", + "apiVersion": "2022-06-01", + "name": "DCR-Win-Perf-to-LAW", + "location": "[parameters('location')]", + "kind": "Windows", "properties": { - "category": "CognitiveSearch", - "target": "[if(parameters('searchEnabled'), format('https://{0}.search.windows.net/', parameters('nameFormatted')), '')]", - "authType": "AAD", - "isSharedToAll": true, - "metadata": { - "ApiType": "Azure", - "ResourceId": "[if(parameters('searchEnabled'), resourceId('Microsoft.Search/searchServices', parameters('nameFormatted')), '')]", - "location": "[if(parameters('searchEnabled'), reference(resourceId('Microsoft.Search/searchServices', parameters('nameFormatted')), '2024-06-01-preview', 'full').location, '')]" + "dataFlows": [ + { + "destinations": [ + "logAnalytics" + ], + "streams": [ + "Microsoft-Perf" + ] + } + ], + "dataSources": { + "performanceCounters": [ + { + "counterSpecifiers": [ + "\\Processor Information(_Total)\\% Processor Time", + "\\Processor Information(_Total)\\% Privileged Time", + "\\Processor Information(_Total)\\% User Time", + "\\Processor Information(_Total)\\Processor Frequency", + "\\System\\Processes", + "\\Process(_Total)\\Thread Count", + "\\Process(_Total)\\Handle Count", + "\\System\\System Up Time", + "\\System\\Context Switches/sec", + "\\System\\Processor Queue Length", + "\\Memory\\% Committed Bytes In Use", + "\\Memory\\Available Bytes", + "\\Memory\\Committed Bytes", + "\\Memory\\Cache Bytes", + "\\Memory\\Pool Paged Bytes", + "\\Memory\\Pool Nonpaged Bytes", + "\\Memory\\Pages/sec", + "\\Memory\\Page Faults/sec", + "\\Process(_Total)\\Working Set", + "\\Process(_Total)\\Working Set - Private", + "\\LogicalDisk(_Total)\\% Disk Time", + "\\LogicalDisk(_Total)\\% Disk Read Time", + "\\LogicalDisk(_Total)\\% Disk Write Time", + "\\LogicalDisk(_Total)\\% Idle Time", + "\\LogicalDisk(_Total)\\Disk Bytes/sec", + "\\LogicalDisk(_Total)\\Disk Read Bytes/sec", + "\\LogicalDisk(_Total)\\Disk Write Bytes/sec", + "\\LogicalDisk(_Total)\\Disk Transfers/sec", + "\\LogicalDisk(_Total)\\Disk Reads/sec", + "\\LogicalDisk(_Total)\\Disk Writes/sec", + "\\LogicalDisk(_Total)\\Avg. Disk sec/Transfer", + "\\LogicalDisk(_Total)\\Avg. Disk sec/Read", + "\\LogicalDisk(_Total)\\Avg. Disk sec/Write", + "\\LogicalDisk(_Total)\\Avg. Disk Queue Length", + "\\LogicalDisk(_Total)\\Avg. Disk Read Queue Length", + "\\LogicalDisk(_Total)\\Avg. Disk Write Queue Length", + "\\LogicalDisk(_Total)\\% Free Space", + "\\LogicalDisk(_Total)\\Free Megabytes", + "\\Network Interface(*)\\Bytes Total/sec", + "\\Network Interface(*)\\Bytes Sent/sec", + "\\Network Interface(*)\\Bytes Received/sec", + "\\Network Interface(*)\\Packets/sec", + "\\Network Interface(*)\\Packets Sent/sec", + "\\Network Interface(*)\\Packets Received/sec", + "\\Network Interface(*)\\Packets Outbound Errors", + "\\Network Interface(*)\\Packets Received Errors" + ], + "name": "perfCounterDataSource60", + "samplingFrequencyInSeconds": 60, + "streams": [ + "Microsoft-Perf" + ] + } + ] + }, + "description": "Collect Performance Counters and send to Azure Monitor Logs.", + "destinations": { + "logAnalytics": [ + { + "name": "logAnalytics", + "workspaceResourceId": "[parameters('workspaceId')]" + } + ] } }, "dependsOn": [ - "[resourceId('Microsoft.CognitiveServices/accounts/projects', parameters('aiServicesName'), parameters('defaultProjectName'))]" + "[resourceId('Microsoft.Compute/virtualMachines/extensions', parameters('vmName'), 'AADLoginForWindows')]" ] }, { - "condition": "[parameters('cosmosDbEnabled')]", - "type": "Microsoft.CognitiveServices/accounts/projects/connections", - "apiVersion": "2025-04-01-preview", - "name": "[format('{0}/{1}/{2}', parameters('aiServicesName'), parameters('defaultProjectName'), parameters('cosmosDBname'))]", + "type": "Microsoft.Insights/dataCollectionRuleAssociations", + "apiVersion": "2022-06-01", + "scope": "[format('Microsoft.Compute/virtualMachines/{0}', parameters('vmName'))]", + "name": "DCRA-VMSS-WEL-LAW", "properties": { - "category": "CosmosDB", - "target": "[if(parameters('cosmosDbEnabled'), reference(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('cosmosDBname')), '2025-05-01-preview').documentEndpoint, '')]", - "authType": "AAD", - "metadata": { - "ApiType": "Azure", - "ResourceId": "[if(parameters('cosmosDbEnabled'), resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('cosmosDBname')), '')]", - "location": "[if(parameters('cosmosDbEnabled'), reference(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('cosmosDBname')), '2025-05-01-preview', 'full').location, '')]" - } + "description": "Association of data collection rule. Deleting this association will break the data collection for this virtual machine.", + "dataCollectionRuleId": "[resourceId('Microsoft.Insights/dataCollectionRules', 'DCR-Win-Event-Logs-to-LAW')]" }, "dependsOn": [ - "[resourceId('Microsoft.CognitiveServices/accounts/projects', parameters('aiServicesName'), parameters('defaultProjectName'))]" + "[resourceId('Microsoft.Insights/dataCollectionRules', 'DCR-Win-Event-Logs-to-LAW')]", + "[resourceId('Microsoft.Compute/virtualMachines', parameters('vmName'))]" + ] + }, + { + "type": "Microsoft.Insights/dataCollectionRuleAssociations", + "apiVersion": "2022-06-01", + "scope": "[format('Microsoft.Compute/virtualMachines/{0}', parameters('vmName'))]", + "name": "DCRA-VM-PC-LAW", + "properties": { + "description": "Association of data collection rule. Deleting this association will break the data collection for this virtual machine.", + "dataCollectionRuleId": "[resourceId('Microsoft.Insights/dataCollectionRules', 'DCR-Win-Perf-to-LAW')]" + }, + "dependsOn": [ + "[resourceId('Microsoft.Insights/dataCollectionRules', 'DCR-Win-Perf-to-LAW')]", + "[resourceId('Microsoft.Compute/virtualMachines', parameters('vmName'))]" + ] + }, + { + "condition": "[and(parameters('enableMicrosoftEntraIdAuth'), not(empty(parameters('userObjectId'))))]", + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Compute/virtualMachines/{0}', parameters('vmName'))]", + "name": "[guid(resourceId('Microsoft.Compute/virtualMachines', parameters('vmName')), subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1c0163c0-47e6-4577-8991-ea5c82e286e4'), parameters('userObjectId'))]", + "properties": { + "roleDefinitionId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1c0163c0-47e6-4577-8991-ea5c82e286e4')]", + "principalType": "User", + "principalId": "[parameters('userObjectId')]" + }, + "dependsOn": [ + "[resourceId('Microsoft.Compute/virtualMachines', parameters('vmName'))]" ] } ], "outputs": { - "projectId": { + "name": { "type": "string", - "value": "[resourceId('Microsoft.CognitiveServices/accounts/projects', parameters('aiServicesName'), parameters('defaultProjectName'))]" + "value": "[parameters('vmName')]" }, - "projectName": { + "id": { "type": "string", - "value": "[parameters('defaultProjectName')]" + "value": "[resourceId('Microsoft.Compute/virtualMachines', parameters('vmName'))]" + }, + "principalId": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Compute/virtualMachines', parameters('vmName')), '2021-11-01', 'full').identity.principalId]" } } } }, "dependsOn": [ - "aiSearch", - "cognitiveServices", - "cosmosDb", + "logAnalyticsWorkspace", + "network", "storageAccount" ] }, - "aiSearch": { - "condition": "[parameters('searchEnabled')]", + "apim": { + "condition": "[parameters('apiManagementEnabled')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[take(format('{0}-ai-search-deployment', parameters('name')), 64)]", + "name": "[take(format('{0}-apim-deployment', parameters('name')), 64)]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -55924,179 +67170,119 @@ "mode": "Incremental", "parameters": { "name": { - "value": "[format('srch{0}{1}', parameters('name'), variables('resourceToken'))]" + "value": "[toLower(format('apim-{0}{1}', parameters('name'), variables('resourceToken')))]" }, "location": { "value": "[parameters('location')]" }, + "publisherEmail": { + "value": "[parameters('apiManagementPublisherEmail')]" + }, + "publisherName": { + "value": "[format('{0} API Management', parameters('name'))]" + }, + "sku": { + "value": "Developer" + }, "networkIsolation": { "value": "[parameters('networkIsolation')]" }, - "virtualNetworkResourceId": "[if(parameters('networkIsolation'), createObject('value', reference('network').outputs.virtualNetworkId.value), createObject('value', ''))]", - "virtualNetworkSubnetResourceId": "[if(parameters('networkIsolation'), createObject('value', reference('network').outputs.vmSubnetId.value), createObject('value', ''))]", "logAnalyticsWorkspaceResourceId": { "value": "[reference('logAnalyticsWorkspace').outputs.resourceId.value]" }, - "roleAssignments": { - "value": "[union(if(empty(parameters('userObjectId')), createArray(), createArray(createObject('principalId', parameters('userObjectId'), 'principalType', 'User', 'roleDefinitionIdOrName', 'Search Index Data Contributor'), createObject('principalId', parameters('userObjectId'), 'principalType', 'User', 'roleDefinitionIdOrName', 'Search Index Data Reader'))), createArray(createObject('principalId', reference('cognitiveServices').outputs.aiServicesSystemAssignedMIPrincipalId.value, 'principalType', 'ServicePrincipal', 'roleDefinitionIdOrName', 'Search Index Data Contributor'), createObject('principalId', reference('cognitiveServices').outputs.aiServicesSystemAssignedMIPrincipalId.value, 'principalType', 'ServicePrincipal', 'roleDefinitionIdOrName', 'Search Service Contributor')))]" - }, + "virtualNetworkResourceId": "[if(parameters('networkIsolation'), createObject('value', reference('network').outputs.resourceId.value), createObject('value', ''))]", + "virtualNetworkSubnetResourceId": "[if(parameters('networkIsolation'), createObject('value', reference('network').outputs.defaultSubnetResourceId.value), createObject('value', ''))]", "tags": { "value": "[variables('allTags')]" } }, "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.1.42791", - "templateHash": "7886886176219744151" - } - }, - "definitions": { - "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.5.1" - } - } + "templateHash": "9207373860269569676" } }, "parameters": { "name": { "type": "string", "metadata": { - "description": "Name of the AI Search resource." + "description": "The name of the API Management service." } }, "location": { "type": "string", "metadata": { - "description": "Specifies the location for all the Azure resources." + "description": "The location of the API Management service." } }, - "tags": { - "type": "object", - "defaultValue": {}, + "publisherName": { + "type": "string", "metadata": { - "description": "Optional. Tags to be applied to the resources." + "description": "Name of the API Management publisher." } }, - "virtualNetworkResourceId": { + "publisherEmail": { "type": "string", "metadata": { - "description": "Resource ID of the virtual network to link the private DNS zones." + "description": "The email address of the API Management publisher." } }, - "virtualNetworkSubnetResourceId": { + "sku": { "type": "string", + "allowedValues": [ + "Consumption", + "Developer", + "Basic", + "Standard", + "Premium", + "StandardV2", + "BasicV2" + ], "metadata": { - "description": "Resource ID of the subnet for the private endpoint." + "description": "Optional. The pricing tier of this API Management service." + } + }, + "networkIsolation": { + "type": "bool", + "metadata": { + "description": "Specifies whether to create a private endpoint for the API Management service." } }, "logAnalyticsWorkspaceResourceId": { "type": "string", "metadata": { - "description": "Resource ID of the Log Analytics workspace to use for diagnostic settings." + "description": "The resource ID of the Log Analytics workspace to use for diagnostic settings." } }, - "networkIsolation": { - "type": "bool", - "defaultValue": true, + "virtualNetworkResourceId": { + "type": "string", "metadata": { - "description": "Specifies whether network isolation is enabled. This will create a private endpoint for the AI Search resource and link the private DNS zone." + "description": "Resource ID of the virtual network to link the private DNS zones." } }, - "roleAssignments": { - "type": "array", - "items": { - "$ref": "#/definitions/roleAssignmentType" - }, - "nullable": true, + "virtualNetworkSubnetResourceId": { + "type": "string", "metadata": { - "description": "Optional. Array of role assignments to create." + "description": "Resource ID of the subnet for the private endpoint." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional tags to be applied to the resources." } } }, - "variables": { - "nameFormatted": "[take(toLower(parameters('name')), 60)]" - }, - "resources": { - "privateDnsZone": { - "condition": "[parameters('networkIsolation')]", + "resources": [ + { "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "private-dns-search-deployment", + "name": "[take(format('{0}-apim-deployment', parameters('name')), 64)]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -56104,17 +67290,107 @@ "mode": "Incremental", "parameters": { "name": { - "value": "privatelink.search.windows.net" + "value": "[parameters('name')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "sku": { + "value": "[parameters('sku')]" + }, + "publisherEmail": { + "value": "[parameters('publisherEmail')]" + }, + "publisherName": { + "value": "[parameters('publisherName')]" + }, + "virtualNetworkType": "[if(parameters('networkIsolation'), createObject('value', 'Internal'), createObject('value', 'None'))]", + "managedIdentities": { + "value": { + "systemAssigned": true + } + }, + "apis": { + "value": [ + { + "apiVersionSet": { + "name": "echo-version-set", + "properties": { + "description": "An echo API version set", + "displayName": "Echo version set", + "versioningScheme": "Segment" + } + }, + "description": "An echo API service", + "displayName": "Echo API", + "name": "echo-api", + "path": "echo", + "protocols": [ + "https" + ], + "serviceUrl": "https://echoapi.cloudapp.net/api" + } + ] }, - "virtualNetworkLinks": { + "customProperties": { + "value": { + "Microsoft.WindowsAzure.ApiManagement.Gateway.Protocols.Server.Http2": "True", + "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Ssl30": "False", + "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Tls10": "False", + "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Tls11": "False", + "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA": "False", + "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA": "False", + "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_CBC_SHA": "False", + "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_CBC_SHA256": "False", + "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_GCM_SHA256": "False", + "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_256_CBC_SHA": "False", + "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_256_CBC_SHA256": "False", + "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TripleDes168": "False", + "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Ssl30": "False", + "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Tls10": "False", + "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Tls11": "False" + } + }, + "diagnosticSettings": { "value": [ { - "virtualNetworkResourceId": "[parameters('virtualNetworkResourceId')]" + "workspaceResourceId": "[parameters('logAnalyticsWorkspaceResourceId')]" } ] }, - "tags": { - "value": "[parameters('tags')]" + "products": { + "value": [ + { + "apis": [ + { + "name": "echo-api" + } + ], + "approvalRequired": true, + "description": "This is an echo API", + "displayName": "Echo API", + "groups": [ + { + "name": "developers" + } + ], + "name": "Starter", + "subscriptionRequired": true, + "terms": "By accessing or using the services provided by Echo API through Azure API Management, you agree to be bound by these Terms of Use. These terms may be updated from time to time, and your continued use of the services constitutes acceptance of any changes." + } + ] + }, + "subscriptions": { + "value": [ + { + "displayName": "testArmSubscriptionAllApis", + "name": "testArmSubscriptionAllApis", + "scope": "/apis" + } + ] } }, "template": { @@ -56124,857 +67400,2058 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "83178825086050429" + "version": "0.34.1.11899", + "templateHash": "7370685983298413612" }, - "name": "Private DNS Zones", - "description": "This module deploys a Private DNS zone.", - "owner": "Azure/module-maintainers" + "name": "API Management Services", + "description": "This module deploys an API Management Service. The default deployment is set to use a Premium SKU to align with Microsoft WAF-aligned best practices. In most cases, non-prod deployments should use a lower-tier SKU." }, "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } + "authorizationServerType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Identifier of the authorization server." + } + }, + "displayName": { + "type": "string", + "maxLength": 50, + "metadata": { + "description": "Required. API Management Service Authorization Servers name. Must be 1 to 50 characters long." + } + }, + "authorizationEndpoint": { + "type": "string", + "metadata": { + "description": "Required. OAuth authorization endpoint. See ." + } + }, + "authorizationMethods": { + "type": "array", + "items": { + "type": "string" }, - "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'." - } + "nullable": true, + "metadata": { + "description": "Optional. HTTP verbs supported by the authorization endpoint. GET must be always present. POST is optional. - HEAD, OPTIONS, TRACE, GET, POST, PUT, PATCH, DELETE." + } + }, + "bearerTokenSendingMethods": { + "type": "array", + "items": { + "type": "string" }, - "principalId": { - "type": "string", - "metadata": { - "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." - } + "nullable": true, + "metadata": { + "description": "Optional. Specifies the mechanism by which access token is passed to the API. - authorizationHeader or query." + } + }, + "clientAuthenticationMethod": { + "type": "array", + "items": { + "type": "string" }, - "principalType": { - "type": "string", - "allowedValues": [ - "Device", - "ForeignGroup", - "Group", - "ServicePrincipal", - "User" - ], - "nullable": true, - "metadata": { - "description": "Optional. The principal type of the assigned principal ID." - } + "nullable": true, + "metadata": { + "description": "Optional. Method of authentication supported by the token endpoint of this authorization server. Possible values are Basic and/or Body. When Body is specified, client credentials and other parameters are passed within the request body in the application/x-www-form-urlencoded format. - Basic or Body." + } + }, + "clientId": { + "type": "securestring", + "metadata": { + "description": "Required. Client or app ID registered with this authorization server." + } + }, + "clientRegistrationEndpoint": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Optional reference to a page where client or app registration for this authorization server is performed. Contains absolute URL to entity being referenced." + } + }, + "clientSecret": { + "type": "securestring", + "metadata": { + "description": "Required. Client or app secret registered with this authorization server. This property will not be filled on 'GET' operations! Use '/listSecrets' POST request to get the value." + } + }, + "defaultScope": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Access token scope that is going to be requested by default. Can be overridden at the API level. Should be provided in the form of a string containing space-delimited values." + } + }, + "serverDescription": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Description of the authorization server. Can contain HTML formatting tags." + } + }, + "grantTypes": { + "type": "array", + "allowedValues": [ + "authorizationCode", + "clientCredentials", + "implicit", + "resourceOwnerPassword" + ], + "metadata": { + "description": "Required. Form of an authorization grant, which the client uses to request the access token. - authorizationCode, implicit, resourceOwnerPassword, clientCredentials." + } + }, + "resourceOwnerPassword": { + "type": "securestring", + "nullable": true, + "metadata": { + "description": "Optional. Can be optionally specified when resource owner password grant type is supported by this authorization server. Default resource owner password." + } + }, + "resourceOwnerUsername": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Can be optionally specified when resource owner password grant type is supported by this authorization server. Default resource owner username." + } + }, + "supportState": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. If true, authorization server will include state parameter from the authorization request to its response. Client may use state parameter to raise protocol security." + } + }, + "tokenBodyParameters": { + "type": "array", + "items": { + "$ref": "#/definitions/tokenBodyParameterType" }, - "description": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The description of the role assignment." + "nullable": true, + "metadata": { + "description": "Optional. Additional parameters required by the token endpoint of this authorization server represented as an array of JSON objects with name and value string properties, i.e. {\"name\" : \"name value\", \"value\": \"a value\"}. - TokenBodyParameterContract object." + } + }, + "tokenEndpoint": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. OAuth token endpoint. Contains absolute URI to entity being referenced." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for an authorization server." + } + }, + "diagnosticSettingFullType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } } }, - "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\"." + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "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`." + } + } } }, - "conditionVersion": { - "type": "string", - "allowedValues": [ - "2.0" - ], - "nullable": true, - "metadata": { - "description": "Optional. Version of the condition." - } + "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 both logs & metrics are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.1" + } + } + }, + "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." + } + } + }, + "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.4.1" + } + } + }, + "managedIdentityAllType": { + "type": "object", + "properties": { + "systemAssigned": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enables system assigned managed identity on the resource." + } + }, + "userAssignedResourceIds": { + "type": "array", + "items": { + "type": "string" }, - "delegatedManagedIdentityResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The Resource Id of the delegated managed identity resource." - } + "nullable": true, + "metadata": { + "description": "Optional. The resource ID(s) to assign to the resource. Required if a user assigned identity is used for encryption." } } }, - "nullable": true + "metadata": { + "description": "An AVM-aligned type for a managed identity configuration. To be used if both a system-assigned & user-assigned identities are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.1" + } + } + }, + "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.4.1" + } + } }, - "lockType": { + "tokenBodyParameterType": { "type": "object", "properties": { "name": { "type": "string", - "nullable": true, "metadata": { - "description": "Optional. Specify the name of lock." + "description": "Required. Body parameter name." } }, - "kind": { + "value": { "type": "string", - "allowedValues": [ - "CanNotDelete", - "None", - "ReadOnly" - ], - "nullable": true, "metadata": { - "description": "Optional. Specify the type of lock." + "description": "Required. Body parameter value." } } }, - "nullable": true + "metadata": { + "description": "The type for a token body parameter.", + "__bicep_imported_from!": { + "sourceTemplate": "authorization-server/main.bicep" + } + } + } + }, + "parameters": { + "additionalLocations": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Additional datacenter locations of the API Management service. Not supported with V2 SKUs." + } }, - "aType": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the API Management service." + } + }, + "certificates": { + "type": "array", + "defaultValue": [], + "maxLength": 10, + "metadata": { + "description": "Optional. List of Certificates that need to be installed in the API Management service. Max supported certificates that can be installed is 10." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "customProperties": { + "type": "object", + "defaultValue": { + "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TripleDes168": "False", + "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_CBC_SHA": "False", + "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_256_CBC_SHA": "False", + "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_CBC_SHA256": "False", + "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA": "False", + "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_256_CBC_SHA256": "False", + "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA": "False", + "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_GCM_SHA256": "False" + }, + "metadata": { + "description": "Optional. Custom properties of the API Management service. Not supported if SKU is Consumption." + } + }, + "disableGateway": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Property only valid for an API Management service deployed in multiple locations. This can be used to disable the gateway in master region." + } + }, + "enableClientCertificate": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Property only meant to be used for Consumption SKU Service. This enforces a client certificate to be presented on each request to the gateway. This also enables the ability to authenticate the certificate in the policy on the gateway." + } + }, + "hostnameConfigurations": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Custom hostname configuration of the API Management service." + } + }, + "managedIdentities": { + "$ref": "#/definitions/managedIdentityAllType", + "nullable": true, + "metadata": { + "description": "Optional. The managed identity definition for this resource." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all Resources." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "nullable": true, + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "minApiVersion": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Limit control plane API calls to API Management service with version equal to or newer than this value." + } + }, + "notificationSenderEmail": { + "type": "string", + "defaultValue": "apimgmt-noreply@mail.windowsazure.com", + "metadata": { + "description": "Optional. The notification sender email address for the service." + } + }, + "publisherEmail": { + "type": "string", + "metadata": { + "description": "Required. The email address of the owner of the service." + } + }, + "publisherName": { + "type": "string", + "metadata": { + "description": "Required. The name of the owner of the service." + } + }, + "restore": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Undelete API Management Service if it was previously soft-deleted. If this flag is specified and set to True all other properties will be ignored." + } + }, + "roleAssignments": { "type": "array", "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } - }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "sku": { + "type": "string", + "defaultValue": "Premium", + "allowedValues": [ + "Consumption", + "Developer", + "Basic", + "Standard", + "Premium", + "StandardV2", + "BasicV2" + ], + "metadata": { + "description": "Optional. The pricing tier of this API Management service." + } + }, + "skuCapacity": { + "type": "int", + "defaultValue": 2, + "metadata": { + "description": "Conditional. The scale units for this API Management service. Required if using Basic, Standard, or Premium skus. For range of capacities for each sku, reference https://azure.microsoft.com/en-us/pricing/details/api-management/." + } + }, + "subnetResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full resource ID of a subnet in a virtual network to deploy the API Management service in." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "virtualNetworkType": { + "type": "string", + "defaultValue": "None", + "allowedValues": [ + "None", + "External", + "Internal" + ], + "metadata": { + "description": "Optional. The type of VPN in which API Management service needs to be configured in. None (Default Value) means the API Management service is not part of any Virtual Network, External means the API Management deployment is set up inside a Virtual Network having an internet Facing Endpoint, and Internal means that API Management deployment is setup inside a Virtual Network having an Intranet Facing Endpoint only." + } + }, + "diagnosticSettings": { + "type": "array", + "items": { + "$ref": "#/definitions/diagnosticSettingFullType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "zones": { + "type": "array", + "defaultValue": [ + 1, + 2 + ], + "metadata": { + "description": "Optional. A list of availability zones denoting where the resource needs to come from. Only supported by Premium sku." + } + }, + "newGuidValue": { + "type": "string", + "defaultValue": "[newGuid()]", + "metadata": { + "description": "Optional. Necessary to create a new GUID." + } + }, + "apis": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. APIs." + } + }, + "apiVersionSets": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. API Version Sets." + } + }, + "authorizationServers": { + "type": "array", + "items": { + "$ref": "#/definitions/authorizationServerType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Authorization servers." + } + }, + "backends": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Backends." + } + }, + "caches": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Caches." + } + }, + "apiDiagnostics": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. API Diagnostics." + } + }, + "identityProviders": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Identity providers." + } + }, + "loggers": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Loggers." + } + }, + "namedValues": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Named values." + } + }, + "policies": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Policies." + } + }, + "portalsettings": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Portal settings." + } + }, + "products": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Products." + } + }, + "subscriptions": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Subscriptions." + } + }, + "publicIpAddressResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Public Standard SKU IP V4 based IP address to be associated with Virtual Network deployed service in the region. Supported only for Developer and Premium SKU being deployed in Virtual Network." + } + }, + "enableDeveloperPortal": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Enable the Developer Portal. The developer portal is not supported on the Consumption SKU." + } + } + }, + "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)))))]" + } + ], + "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", + "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned,UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', 'None')), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", + "builtInRoleNames": { + "API Management Developer Portal Content Editor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c031e6a8-4391-4de0-8d69-4706a7ed3729')]", + "API Management Service Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '312a565d-c81f-4fd8-895a-4e21e48d571c')]", + "API Management Service Operator Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e022efe7-f5ba-4159-bbe4-b44f577e9b61')]", + "API Management Service Reader Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '71522526-b88f-4d52-b57f-d31fc3546d0d')]", + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.apimanagement-service.{0}.{1}', replace('0.9.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" } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." + } + } + } + }, + "service": { + "type": "Microsoft.ApiManagement/service", + "apiVersion": "2024-05-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "sku": { + "name": "[parameters('sku')]", + "capacity": "[if(contains(parameters('sku'), 'Consumption'), 0, if(contains(parameters('sku'), 'Developer'), 1, parameters('skuCapacity')))]" + }, + "zones": "[if(contains(parameters('sku'), 'Premium'), parameters('zones'), createArray())]", + "identity": "[variables('identity')]", + "properties": { + "publisherEmail": "[parameters('publisherEmail')]", + "publisherName": "[parameters('publisherName')]", + "notificationSenderEmail": "[parameters('notificationSenderEmail')]", + "hostnameConfigurations": "[parameters('hostnameConfigurations')]", + "additionalLocations": "[if(contains(parameters('sku'), 'Premium'), parameters('additionalLocations'), createArray())]", + "customProperties": "[if(contains(parameters('sku'), 'Consumption'), null(), parameters('customProperties'))]", + "certificates": "[parameters('certificates')]", + "enableClientCertificate": "[if(parameters('enableClientCertificate'), true(), null())]", + "disableGateway": "[parameters('disableGateway')]", + "virtualNetworkType": "[parameters('virtualNetworkType')]", + "virtualNetworkConfiguration": "[if(not(empty(parameters('subnetResourceId'))), createObject('subnetResourceId', parameters('subnetResourceId')), null())]", + "publicIpAddressId": "[if(not(empty(parameters('publicIpAddressResourceId'))), parameters('publicIpAddressResourceId'), null())]", + "apiVersionConstraint": "[if(not(empty(parameters('minApiVersion'))), createObject('minApiVersion', parameters('minApiVersion')), createObject('minApiVersion', '2021-08-01'))]", + "restore": "[parameters('restore')]", + "developerPortalStatus": "[if(not(equals(parameters('sku'), 'Consumption')), if(parameters('enableDeveloperPortal'), 'Enabled', 'Disabled'), null())]" + } + }, + "service_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.ApiManagement/service/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "service" + ] + }, + "service_diagnosticSettings": { + "copy": { + "name": "service_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.ApiManagement/service/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null } }, - "aRecords": { - "type": "array", - "items": { - "type": "object", - "properties": { - "ipv4Address": { - "type": "string", - "metadata": { - "description": "Required. The IPv4 address of this A record." - } - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. The list of A records in the record set." + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" } } - } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "service" + ] + }, + "service_roleAssignments": { + "copy": { + "name": "service_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.ApiManagement/service/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.ApiManagement/service', 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')]" }, - "nullable": true + "dependsOn": [ + "service" + ] }, - "aaaaType": { - "type": "array", - "items": { - "type": "object", - "properties": { + "service_apis": { + "copy": { + "name": "service_apis", + "count": "[length(parameters('apis'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Apim-Api-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "apiManagementServiceName": { + "value": "[parameters('name')]" + }, + "displayName": { + "value": "[parameters('apis')[copyIndex()].displayName]" + }, "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } + "value": "[parameters('apis')[copyIndex()].name]" }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } + "path": { + "value": "[parameters('apis')[copyIndex()].path]" }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } + "apiDescription": { + "value": "[tryGet(parameters('apis')[copyIndex()], 'apiDescription')]" }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } + "apiRevision": { + "value": "[tryGet(parameters('apis')[copyIndex()], 'apiRevision')]" }, - "aaaaRecords": { - "type": "array", - "items": { - "type": "object", - "properties": { - "ipv6Address": { - "type": "string", - "metadata": { - "description": "Required. The IPv6 address of this AAAA record." - } - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. The list of AAAA records in the record set." - } - } - } - }, - "nullable": true - }, - "cnameType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } + "apiRevisionDescription": { + "value": "[tryGet(parameters('apis')[copyIndex()], 'apiRevisionDescription')]" }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } + "apiType": { + "value": "[tryGet(parameters('apis')[copyIndex()], 'apiType')]" }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } + "apiVersion": { + "value": "[tryGet(parameters('apis')[copyIndex()], 'apiVersion')]" }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } + "apiVersionDescription": { + "value": "[tryGet(parameters('apis')[copyIndex()], 'apiVersionDescription')]" }, - "cnameRecord": { - "type": "object", - "properties": { - "cname": { - "type": "string", - "metadata": { - "description": "Required. The canonical name of the CNAME record." - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. The CNAME record in the record set." - } - } - } - }, - "nullable": true - }, - "mxType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } + "apiVersionSetId": { + "value": "[tryGet(parameters('apis')[copyIndex()], 'apiVersionSetId')]" }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } + "authenticationSettings": { + "value": "[tryGet(parameters('apis')[copyIndex()], 'authenticationSettings')]" }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } + "format": { + "value": "[tryGet(parameters('apis')[copyIndex()], 'format')]" }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } + "isCurrent": { + "value": "[tryGet(parameters('apis')[copyIndex()], 'isCurrent')]" }, - "mxRecords": { - "type": "array", - "items": { - "type": "object", - "properties": { - "exchange": { - "type": "string", - "metadata": { - "description": "Required. The domain name of the mail host for this MX record." - } - }, - "preference": { - "type": "int", - "metadata": { - "description": "Required. The preference value for this MX record." - } - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. The list of MX records in the record set." - } - } - } - }, - "nullable": true - }, - "ptrType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } + "protocols": { + "value": "[tryGet(parameters('apis')[copyIndex()], 'protocols')]" }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } + "policies": { + "value": "[tryGet(parameters('apis')[copyIndex()], 'policies')]" }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } + "serviceUrl": { + "value": "[tryGet(parameters('apis')[copyIndex()], 'serviceUrl')]" }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } + "sourceApiId": { + "value": "[tryGet(parameters('apis')[copyIndex()], 'sourceApiId')]" }, - "ptrRecords": { - "type": "array", - "items": { - "type": "object", - "properties": { - "ptrdname": { - "type": "string", - "metadata": { - "description": "Required. The PTR target domain name for this PTR record." - } - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. The list of PTR records in the record set." - } - } - } - }, - "nullable": true - }, - "soaType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } + "subscriptionKeyParameterNames": { + "value": "[tryGet(parameters('apis')[copyIndex()], 'subscriptionKeyParameterNames')]" }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } + "subscriptionRequired": { + "value": "[tryGet(parameters('apis')[copyIndex()], 'subscriptionRequired')]" }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } + "type": { + "value": "[tryGet(parameters('apis')[copyIndex()], 'type')]" }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } + "value": { + "value": "[tryGet(parameters('apis')[copyIndex()], 'value')]" }, - "soaRecord": { - "type": "object", - "properties": { - "email": { - "type": "string", - "metadata": { - "description": "Required. The email contact for this SOA record." - } - }, - "expireTime": { - "type": "int", - "metadata": { - "description": "Required. The expire time for this SOA record." - } - }, - "host": { - "type": "string", - "metadata": { - "description": "Required. The domain name of the authoritative name server for this SOA record." - } - }, - "minimumTtl": { - "type": "int", - "metadata": { - "description": "Required. The minimum value for this SOA record. By convention this is used to determine the negative caching duration." - } - }, - "refreshTime": { - "type": "int", - "metadata": { - "description": "Required. The refresh value for this SOA record." - } - }, - "retryTime": { - "type": "int", - "metadata": { - "description": "Required. The retry time for this SOA record." - } - }, - "serialNumber": { - "type": "int", - "metadata": { - "description": "Required. The serial number for this SOA record." - } + "wsdlSelector": { + "value": "[tryGet(parameters('apis')[copyIndex()], 'wsdlSelector')]" + } + }, + "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.34.1.11899", + "templateHash": "7731867308363152438" + }, + "name": "API Management Service APIs", + "description": "This module deploys an API Management Service API." + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. API revision identifier. Must be unique in the current API Management service instance. Non-current revision has ;rev=n as a suffix where n is the revision number." + } + }, + "policies": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. Array of Policies to apply to the Service API." + } + }, + "diagnostics": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. Array of diagnostics to apply to the Service API." + } + }, + "apiManagementServiceName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent API Management service. Required if the template is used in a standalone deployment." + } + }, + "apiRevision": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Describes the Revision of the API. If no value is provided, default revision 1 is created." + } + }, + "apiRevisionDescription": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Description of the API Revision." + } + }, + "apiType": { + "type": "string", + "defaultValue": "http", + "allowedValues": [ + "graphql", + "http", + "soap", + "websocket" + ], + "metadata": { + "description": "Optional. Type of API to create. * http creates a REST API * soap creates a SOAP pass-through API * websocket creates websocket API * graphql creates GraphQL API." + } + }, + "apiVersion": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Indicates the Version identifier of the API if the API is versioned." + } + }, + "apiVersionSetId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Indicates the Version identifier of the API version set." + } + }, + "apiVersionDescription": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Description of the API Version." + } + }, + "authenticationSettings": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Collection of authentication settings included into this API." + } + }, + "apiDescription": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Description of the API. May include HTML formatting tags." + } + }, + "displayName": { + "type": "string", + "maxLength": 300, + "metadata": { + "description": "Required. API name. Must be 1 to 300 characters long." + } + }, + "format": { + "type": "string", + "defaultValue": "openapi", + "allowedValues": [ + "wadl-xml", + "wadl-link-json", + "swagger-json", + "swagger-link-json", + "wsdl", + "wsdl-link", + "openapi", + "openapi+json", + "openapi-link", + "openapi+json-link" + ], + "metadata": { + "description": "Optional. Format of the Content in which the API is getting imported." + } + }, + "isCurrent": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Indicates if API revision is current API revision." + } + }, + "loggerName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Conditional. The name of the API management service logger. Required if using api/diagnostics." + } + }, + "path": { + "type": "string", + "metadata": { + "description": "Required. Relative URL uniquely identifying this API and all of its resource paths within the API Management service instance. It is appended to the API endpoint base URL specified during the service instance creation to form a public URL for this API." + } + }, + "protocols": { + "type": "array", + "defaultValue": [ + "https" + ], + "metadata": { + "description": "Optional. Describes on which protocols the operations in this API can be invoked. - HTTP or HTTPS." + } + }, + "serviceUrl": { + "type": "string", + "nullable": true, + "maxLength": 2000, + "metadata": { + "description": "Optional. Absolute URL of the backend service implementing this API. Cannot be more than 2000 characters long." + } + }, + "sourceApiId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. API identifier of the source API." } }, - "nullable": true, - "metadata": { - "description": "Optional. The SOA record in the record set." - } - } - } - }, - "nullable": true - }, - "srvType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } - }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." + "subscriptionKeyParameterNames": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Protocols over which API is made available." + } + }, + "subscriptionRequired": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Specifies whether an API or Product subscription is required for accessing the API." + } + }, + "type": { + "type": "string", + "defaultValue": "http", + "allowedValues": [ + "graphql", + "http", + "soap", + "websocket" + ], + "metadata": { + "description": "Optional. Type of API." + } + }, + "value": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Content value when Importing an API." + } + }, + "wsdlSelector": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Criteria to limit import of WSDL to a subset of the document." + } } }, - "srvRecords": { - "type": "array", - "items": { - "type": "object", + "resources": { + "service": { + "existing": true, + "type": "Microsoft.ApiManagement/service", + "apiVersion": "2023-05-01-preview", + "name": "[parameters('apiManagementServiceName')]" + }, + "api": { + "type": "Microsoft.ApiManagement/service/apis", + "apiVersion": "2022-08-01", + "name": "[format('{0}/{1}', parameters('apiManagementServiceName'), parameters('name'))]", "properties": { - "priority": { - "type": "int", - "metadata": { - "description": "Required. The priority value for this SRV record." - } - }, - "weight": { - "type": "int", - "metadata": { - "description": "Required. The weight value for this SRV record." - } + "apiRevision": "[parameters('apiRevision')]", + "apiRevisionDescription": "[parameters('apiRevisionDescription')]", + "apiType": "[parameters('apiType')]", + "apiVersion": "[parameters('apiVersion')]", + "apiVersionDescription": "[parameters('apiVersionDescription')]", + "apiVersionSetId": "[parameters('apiVersionSetId')]", + "authenticationSettings": "[coalesce(parameters('authenticationSettings'), createObject())]", + "description": "[coalesce(parameters('apiDescription'), '')]", + "displayName": "[parameters('displayName')]", + "format": "[if(not(empty(parameters('value'))), parameters('format'), null())]", + "isCurrent": "[parameters('isCurrent')]", + "path": "[parameters('path')]", + "protocols": "[parameters('protocols')]", + "serviceUrl": "[parameters('serviceUrl')]", + "sourceApiId": "[parameters('sourceApiId')]", + "subscriptionKeyParameterNames": "[parameters('subscriptionKeyParameterNames')]", + "subscriptionRequired": "[parameters('subscriptionRequired')]", + "type": "[parameters('type')]", + "value": "[parameters('value')]", + "wsdlSelector": "[coalesce(parameters('wsdlSelector'), createObject())]" + } + }, + "policy": { + "copy": { + "name": "policy", + "count": "[length(coalesce(parameters('policies'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Policy-{1}', deployment().name, copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" }, - "port": { - "type": "int", - "metadata": { - "description": "Required. The port value for this SRV record." + "mode": "Incremental", + "parameters": { + "apiManagementServiceName": { + "value": "[parameters('apiManagementServiceName')]" + }, + "apiName": { + "value": "[parameters('name')]" + }, + "format": { + "value": "[coalesce(tryGet(coalesce(parameters('policies'), createArray())[copyIndex()], 'format'), 'xml')]" + }, + "value": { + "value": "[coalesce(parameters('policies'), createArray())[copyIndex()].value]" } }, - "target": { - "type": "string", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", "metadata": { - "description": "Required. The target domain name for this SRV record." + "_generator": { + "name": "bicep", + "version": "0.34.1.11899", + "templateHash": "3121557432730960376" + }, + "name": "API Management Service APIs Policies", + "description": "This module deploys an API Management Service API Policy." + }, + "parameters": { + "apiManagementServiceName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent API Management service. Required if the template is used in a standalone deployment." + } + }, + "apiName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent API. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "defaultValue": "policy", + "metadata": { + "description": "Optional. The name of the policy." + } + }, + "format": { + "type": "string", + "defaultValue": "xml", + "allowedValues": [ + "rawxml", + "rawxml-link", + "xml", + "xml-link" + ], + "metadata": { + "description": "Optional. Format of the policyContent." + } + }, + "value": { + "type": "string", + "metadata": { + "description": "Required. Contents of the Policy as defined by the format." + } + } + }, + "resources": [ + { + "type": "Microsoft.ApiManagement/service/apis/policies", + "apiVersion": "2022-08-01", + "name": "[format('{0}/{1}/{2}', parameters('apiManagementServiceName'), parameters('apiName'), parameters('name'))]", + "properties": { + "format": "[parameters('format')]", + "value": "[parameters('value')]" + } + } + ], + "outputs": { + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the API policy." + }, + "value": "[resourceId('Microsoft.ApiManagement/service/apis/policies', parameters('apiManagementServiceName'), parameters('apiName'), parameters('name'))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the API policy." + }, + "value": "[parameters('name')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the API policy was deployed into." + }, + "value": "[resourceGroup().name]" + } } } - } + }, + "dependsOn": [ + "api" + ] }, - "nullable": true, - "metadata": { - "description": "Optional. The list of SRV records in the record set." - } - } - } - }, - "nullable": true - }, - "txtType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } - }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "txtRecords": { - "type": "array", - "items": { - "type": "object", + "diagnostic": { + "copy": { + "name": "diagnostic", + "count": "[length(coalesce(parameters('diagnostics'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-diagnostics-{1}', deployment().name, copyIndex())]", "properties": { - "value": { - "type": "array", - "items": { - "type": "string" + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[tryGet(coalesce(parameters('diagnostics'), createArray())[copyIndex()], 'name')]" + }, + "apiManagementServiceName": { + "value": "[parameters('apiManagementServiceName')]" + }, + "apiName": { + "value": "[parameters('name')]" + }, + "loggerName": { + "value": "[parameters('loggerName')]" + }, + "alwaysLog": { + "value": "[tryGet(coalesce(parameters('diagnostics'), createArray())[copyIndex()], 'alwaysLog')]" + }, + "backend": { + "value": "[tryGet(coalesce(parameters('diagnostics'), createArray())[copyIndex()], 'backend')]" + }, + "frontend": { + "value": "[tryGet(coalesce(parameters('diagnostics'), createArray())[copyIndex()], 'frontend')]" + }, + "httpCorrelationProtocol": { + "value": "[tryGet(coalesce(parameters('diagnostics'), createArray())[copyIndex()], 'httpCorrelationProtocol')]" + }, + "logClientIp": { + "value": "[tryGet(coalesce(parameters('diagnostics'), createArray())[copyIndex()], 'logClientIp')]" + }, + "metrics": { + "value": "[tryGet(coalesce(parameters('diagnostics'), createArray())[copyIndex()], 'metrics')]" + }, + "operationNameFormat": { + "value": "[tryGet(coalesce(parameters('diagnostics'), createArray())[copyIndex()], 'operationNameFormat')]" }, + "samplingPercentage": { + "value": "[tryGet(coalesce(parameters('diagnostics'), createArray())[copyIndex()], 'samplingPercentage')]" + }, + "verbosity": { + "value": "[tryGet(coalesce(parameters('diagnostics'), createArray())[copyIndex()], 'verbosity')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", "metadata": { - "description": "Required. The text value of this TXT record." + "_generator": { + "name": "bicep", + "version": "0.34.1.11899", + "templateHash": "220134378763209880" + }, + "name": "API Management Service APIs Diagnostics.", + "description": "This module deploys an API Management Service API Diagnostics." + }, + "parameters": { + "apiManagementServiceName": { + "type": "string", + "metadata": { + "description": "Required. The name of the parent API Management service." + } + }, + "apiName": { + "type": "string", + "metadata": { + "description": "Required. The name of the parent API." + } + }, + "loggerName": { + "type": "string", + "metadata": { + "description": "Required. The name of the logger." + } + }, + "name": { + "type": "string", + "defaultValue": "local", + "allowedValues": [ + "azuremonitor", + "applicationinsights", + "local" + ], + "metadata": { + "description": "Optional. Type of diagnostic resource." + } + }, + "alwaysLog": { + "type": "string", + "defaultValue": "allErrors", + "metadata": { + "description": "Optional. Specifies for what type of messages sampling settings should not apply." + } + }, + "backend": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Diagnostic settings for incoming/outgoing HTTP messages to the Backend." + } + }, + "frontend": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Diagnostic settings for incoming/outgoing HTTP messages to the Gateway." + } + }, + "httpCorrelationProtocol": { + "type": "string", + "defaultValue": "Legacy", + "allowedValues": [ + "Legacy", + "None", + "W3C" + ], + "metadata": { + "description": "Conditional. Sets correlation protocol to use for Application Insights diagnostics. Required if using Application Insights." + } + }, + "logClientIp": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Log the ClientIP." + } + }, + "metrics": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Conditional. Emit custom metrics via emit-metric policy. Required if using Application Insights." + } + }, + "operationNameFormat": { + "type": "string", + "defaultValue": "Name", + "allowedValues": [ + "Name", + "URI" + ], + "metadata": { + "description": "Conditional. The format of the Operation Name for Application Insights telemetries. Required if using Application Insights." + } + }, + "samplingPercentage": { + "type": "int", + "defaultValue": 100, + "metadata": { + "description": "Optional. Rate of sampling for fixed-rate sampling. Specifies the percentage of requests that are logged. 0% sampling means zero requests logged, while 100% sampling means all requests logged." + } + }, + "verbosity": { + "type": "string", + "defaultValue": "error", + "allowedValues": [ + "error", + "information", + "verbose" + ], + "metadata": { + "description": "Optional. The verbosity level applied to traces emitted by trace policies." + } + } + }, + "resources": [ + { + "type": "Microsoft.ApiManagement/service/apis/diagnostics", + "apiVersion": "2022-08-01", + "name": "[format('{0}/{1}/{2}', parameters('apiManagementServiceName'), parameters('apiName'), parameters('name'))]", + "properties": { + "alwaysLog": "[parameters('alwaysLog')]", + "backend": "[parameters('backend')]", + "frontend": "[parameters('frontend')]", + "httpCorrelationProtocol": "[parameters('httpCorrelationProtocol')]", + "logClientIp": "[parameters('logClientIp')]", + "loggerId": "[resourceId('Microsoft.ApiManagement/service/loggers', parameters('apiManagementServiceName'), parameters('loggerName'))]", + "metrics": "[parameters('metrics')]", + "operationNameFormat": "[parameters('operationNameFormat')]", + "sampling": { + "percentage": "[parameters('samplingPercentage')]", + "samplingType": "fixed" + }, + "verbosity": "[parameters('verbosity')]" + } + } + ], + "outputs": { + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the API diagnostic." + }, + "value": "[resourceId('Microsoft.ApiManagement/service/apis/diagnostics', parameters('apiManagementServiceName'), parameters('apiName'), parameters('name'))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the API diagnostic." + }, + "value": "[parameters('name')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the API diagnostic was deployed into." + }, + "value": "[resourceGroup().name]" + } } } - } + }, + "dependsOn": [ + "api" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the API management service API." + }, + "value": "[parameters('name')]" }, - "nullable": true, - "metadata": { - "description": "Optional. The list of TXT records in the record set." + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the API management service API." + }, + "value": "[resourceId('Microsoft.ApiManagement/service/apis', parameters('apiManagementServiceName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the API management service API was deployed to." + }, + "value": "[resourceGroup().name]" } } } }, - "nullable": true + "dependsOn": [ + "service", + "service_apiVersionSets" + ] }, - "virtualNetworkLinkType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "minLength": 1, - "maxLength": 80, - "metadata": { - "description": "Optional. The resource name." - } + "service_apiVersionSets": { + "copy": { + "name": "service_apiVersionSets", + "count": "[length(parameters('apiVersionSets'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Apim-ApiVersionSet-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "apiManagementServiceName": { + "value": "[parameters('name')]" }, - "virtualNetworkResourceId": { - "type": "string", - "metadata": { - "description": "Required. The resource ID of the virtual network to link." - } + "name": { + "value": "[parameters('apiVersionSets')[copyIndex()].name]" }, - "location": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The Azure Region where the resource lives." - } + "properties": { + "value": "[coalesce(tryGet(parameters('apiVersionSets')[copyIndex()], 'properties'), createObject())]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.34.1.11899", + "templateHash": "6536427264308776904" + }, + "name": "API Management Service API Version Sets", + "description": "This module deploys an API Management Service API Version Set." }, - "registrationEnabled": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Is auto-registration of virtual machine records in the virtual network in the Private DNS zone enabled?." + "parameters": { + "apiManagementServiceName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent API Management service. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "defaultValue": "default", + "metadata": { + "description": "Optional. API Version set name." + } + }, + "properties": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. API Version set properties." + } } }, - "tags": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Resource tags." + "resources": [ + { + "type": "Microsoft.ApiManagement/service/apiVersionSets", + "apiVersion": "2022-08-01", + "name": "[format('{0}/{1}', parameters('apiManagementServiceName'), parameters('name'))]", + "properties": "[parameters('properties')]" } - }, - "resolutionPolicy": { - "type": "string", - "allowedValues": [ - "Default", - "NxDomainRedirect" - ], - "nullable": true, - "metadata": { - "description": "Optional. The resolution type of the private-dns-zone fallback machanism." + ], + "outputs": { + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the API Version set." + }, + "value": "[resourceId('Microsoft.ApiManagement/service/apiVersionSets', parameters('apiManagementServiceName'), parameters('name'))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the API Version set." + }, + "value": "[parameters('name')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the API Version set was deployed into." + }, + "value": "[resourceGroup().name]" } } } }, - "nullable": true - } - }, - "parameters": { - "name": { - "type": "string", - "metadata": { - "description": "Required. Private DNS zone name." - } - }, - "a": { - "$ref": "#/definitions/aType", - "metadata": { - "description": "Optional. Array of A records." - } - }, - "aaaa": { - "$ref": "#/definitions/aaaaType", - "metadata": { - "description": "Optional. Array of AAAA records." - } - }, - "cname": { - "$ref": "#/definitions/cnameType", - "metadata": { - "description": "Optional. Array of CNAME records." - } - }, - "mx": { - "$ref": "#/definitions/mxType", - "metadata": { - "description": "Optional. Array of MX records." - } - }, - "ptr": { - "$ref": "#/definitions/ptrType", - "metadata": { - "description": "Optional. Array of PTR records." - } - }, - "soa": { - "$ref": "#/definitions/soaType", - "metadata": { - "description": "Optional. Array of SOA records." - } - }, - "srv": { - "$ref": "#/definitions/srvType", - "metadata": { - "description": "Optional. Array of SRV records." - } - }, - "txt": { - "$ref": "#/definitions/txtType", - "metadata": { - "description": "Optional. Array of TXT records." - } - }, - "virtualNetworkLinks": { - "$ref": "#/definitions/virtualNetworkLinkType", - "metadata": { - "description": "Optional. Array of custom objects describing vNet links of the DNS zone. Each object should contain properties 'virtualNetworkResourceId' and 'registrationEnabled'. The 'vnetResourceId' is a resource ID of a vNet to link, 'registrationEnabled' (bool) enables automatic DNS registration in the zone for the linked vNet." - } - }, - "location": { - "type": "string", - "defaultValue": "global", - "metadata": { - "description": "Optional. The location of the PrivateDNSZone. Should be global." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "tags": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Tags of the resource." - } - }, - "lock": { - "$ref": "#/definitions/lockType", - "metadata": { - "description": "Optional. The lock settings of the service." - } + "dependsOn": [ + "service" + ] }, - "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')]", - "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')]", + "service_authorizationServers": { + "copy": { + "name": "service_authorizationServers", + "count": "[length(coalesce(parameters('authorizationServers'), createArray()))]" + }, "type": "Microsoft.Resources/deployments", - "apiVersion": "2024-03-01", - "name": "[format('46d3xbcp.res.network-privatednszone.{0}.{1}', replace('0.7.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Apim-AuthorizationServer-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, "mode": "Incremental", + "parameters": { + "apiManagementServiceName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('authorizationServers'), createArray())[copyIndex()].name]" + }, + "displayName": { + "value": "[coalesce(parameters('authorizationServers'), createArray())[copyIndex()].displayName]" + }, + "authorizationEndpoint": { + "value": "[coalesce(parameters('authorizationServers'), createArray())[copyIndex()].authorizationEndpoint]" + }, + "authorizationMethods": { + "value": "[coalesce(tryGet(coalesce(parameters('authorizationServers'), createArray())[copyIndex()], 'authorizationMethods'), createArray('GET'))]" + }, + "bearerTokenSendingMethods": { + "value": "[coalesce(tryGet(coalesce(parameters('authorizationServers'), createArray())[copyIndex()], 'bearerTokenSendingMethods'), createArray('authorizationHeader'))]" + }, + "clientAuthenticationMethod": { + "value": "[coalesce(tryGet(coalesce(parameters('authorizationServers'), createArray())[copyIndex()], 'clientAuthenticationMethod'), createArray('Basic'))]" + }, + "clientId": { + "value": "[coalesce(parameters('authorizationServers'), createArray())[copyIndex()].clientId]" + }, + "clientSecret": { + "value": "[coalesce(parameters('authorizationServers'), createArray())[copyIndex()].clientSecret]" + }, + "clientRegistrationEndpoint": { + "value": "[coalesce(tryGet(coalesce(parameters('authorizationServers'), createArray())[copyIndex()], 'clientRegistrationEndpoint'), '')]" + }, + "defaultScope": { + "value": "[coalesce(tryGet(coalesce(parameters('authorizationServers'), createArray())[copyIndex()], 'defaultScope'), '')]" + }, + "grantTypes": { + "value": "[coalesce(parameters('authorizationServers'), createArray())[copyIndex()].grantTypes]" + }, + "resourceOwnerPassword": { + "value": "[coalesce(tryGet(coalesce(parameters('authorizationServers'), createArray())[copyIndex()], 'resourceOwnerPassword'), '')]" + }, + "resourceOwnerUsername": { + "value": "[coalesce(tryGet(coalesce(parameters('authorizationServers'), createArray())[copyIndex()], 'resourceOwnerUsername'), '')]" + }, + "serverDescription": { + "value": "[coalesce(tryGet(coalesce(parameters('authorizationServers'), createArray())[copyIndex()], 'serverDescription'), '')]" + }, + "supportState": { + "value": "[coalesce(tryGet(coalesce(parameters('authorizationServers'), createArray())[copyIndex()], 'supportState'), false())]" + }, + "tokenBodyParameters": { + "value": "[coalesce(tryGet(coalesce(parameters('authorizationServers'), createArray())[copyIndex()], 'tokenBodyParameters'), createArray())]" + }, + "tokenEndpoint": { + "value": "[coalesce(tryGet(coalesce(parameters('authorizationServers'), createArray())[copyIndex()], 'tokenEndpoint'), '')]" + } + }, "template": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", "contentVersion": "1.0.0.0", - "resources": [], + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.34.1.11899", + "templateHash": "4005929869264436679" + }, + "name": "API Management Service Authorization Servers", + "description": "This module deploys an API Management Service Authorization Server." + }, + "definitions": { + "tokenBodyParameterType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Body parameter name." + } + }, + "value": { + "type": "string", + "metadata": { + "description": "Required. Body parameter value." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for a token body parameter." + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Identifier of the authorization server." + } + }, + "displayName": { + "type": "string", + "maxLength": 50, + "metadata": { + "description": "Required. API Management Service Authorization Servers name. Must be 1 to 50 characters long." + } + }, + "apiManagementServiceName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent API Management service. Required if the template is used in a standalone deployment." + } + }, + "authorizationEndpoint": { + "type": "string", + "metadata": { + "description": "Required. OAuth authorization endpoint. See ." + } + }, + "authorizationMethods": { + "type": "array", + "defaultValue": [ + "GET" + ], + "metadata": { + "description": "Optional. HTTP verbs supported by the authorization endpoint. GET must be always present. POST is optional. - HEAD, OPTIONS, TRACE, GET, POST, PUT, PATCH, DELETE." + } + }, + "bearerTokenSendingMethods": { + "type": "array", + "defaultValue": [ + "authorizationHeader" + ], + "metadata": { + "description": "Optional. Specifies the mechanism by which access token is passed to the API. - authorizationHeader or query." + } + }, + "clientAuthenticationMethod": { + "type": "array", + "defaultValue": [ + "Basic" + ], + "metadata": { + "description": "Optional. Method of authentication supported by the token endpoint of this authorization server. Possible values are Basic and/or Body. When Body is specified, client credentials and other parameters are passed within the request body in the application/x-www-form-urlencoded format. - Basic or Body." + } + }, + "clientId": { + "type": "securestring", + "metadata": { + "description": "Required. Client or app ID registered with this authorization server." + } + }, + "clientRegistrationEndpoint": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Optional reference to a page where client or app registration for this authorization server is performed. Contains absolute URL to entity being referenced." + } + }, + "clientSecret": { + "type": "securestring", + "metadata": { + "description": "Required. Client or app secret registered with this authorization server. This property will not be filled on 'GET' operations! Use '/listSecrets' POST request to get the value." + } + }, + "defaultScope": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Access token scope that is going to be requested by default. Can be overridden at the API level. Should be provided in the form of a string containing space-delimited values." + } + }, + "serverDescription": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Description of the authorization server. Can contain HTML formatting tags." + } + }, + "grantTypes": { + "type": "array", + "items": { + "type": "string" + }, + "allowedValues": [ + "authorizationCode", + "clientCredentials", + "implicit", + "resourceOwnerPassword" + ], + "metadata": { + "description": "Required. Form of an authorization grant, which the client uses to request the access token. - authorizationCode, implicit, resourceOwnerPassword, clientCredentials." + } + }, + "resourceOwnerPassword": { + "type": "securestring", + "defaultValue": "", + "metadata": { + "description": "Optional. Can be optionally specified when resource owner password grant type is supported by this authorization server. Default resource owner password." + } + }, + "resourceOwnerUsername": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Can be optionally specified when resource owner password grant type is supported by this authorization server. Default resource owner username." + } + }, + "supportState": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. If true, authorization server will include state parameter from the authorization request to its response. Client may use state parameter to raise protocol security." + } + }, + "tokenBodyParameters": { + "type": "array", + "items": { + "$ref": "#/definitions/tokenBodyParameterType" + }, + "defaultValue": [], + "metadata": { + "description": "Optional. Additional parameters required by the token endpoint of this authorization server represented as an array of JSON objects with name and value string properties." + } + }, + "tokenEndpoint": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. OAuth token endpoint. Contains absolute URI to entity being referenced." + } + } + }, + "variables": { + "defaultAuthorizationMethods": [ + "GET" + ], + "setAuthorizationMethods": "[union(parameters('authorizationMethods'), variables('defaultAuthorizationMethods'))]" + }, + "resources": { + "service": { + "existing": true, + "type": "Microsoft.ApiManagement/service", + "apiVersion": "2023-05-01-preview", + "name": "[parameters('apiManagementServiceName')]" + }, + "authorizationServer": { + "type": "Microsoft.ApiManagement/service/authorizationServers", + "apiVersion": "2022-08-01", + "name": "[format('{0}/{1}', parameters('apiManagementServiceName'), parameters('name'))]", + "properties": { + "description": "[parameters('serverDescription')]", + "authorizationMethods": "[variables('setAuthorizationMethods')]", + "clientAuthenticationMethod": "[parameters('clientAuthenticationMethod')]", + "tokenBodyParameters": "[parameters('tokenBodyParameters')]", + "tokenEndpoint": "[parameters('tokenEndpoint')]", + "supportState": "[parameters('supportState')]", + "defaultScope": "[parameters('defaultScope')]", + "bearerTokenSendingMethods": "[parameters('bearerTokenSendingMethods')]", + "resourceOwnerUsername": "[parameters('resourceOwnerUsername')]", + "resourceOwnerPassword": "[parameters('resourceOwnerPassword')]", + "displayName": "[parameters('displayName')]", + "clientRegistrationEndpoint": "[parameters('clientRegistrationEndpoint')]", + "authorizationEndpoint": "[parameters('authorizationEndpoint')]", + "grantTypes": "[parameters('grantTypes')]", + "clientId": "[parameters('clientId')]", + "clientSecret": "[parameters('clientSecret')]" + } + } + }, "outputs": { - "telemetry": { - "type": "String", - "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + "name": { + "type": "string", + "metadata": { + "description": "The name of the API management service authorization server." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the API management service authorization server." + }, + "value": "[resourceId('Microsoft.ApiManagement/service/authorizationServers', parameters('apiManagementServiceName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the API management service authorization server was deployed into." + }, + "value": "[resourceGroup().name]" } } } - } - }, - "privateDnsZone": { - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('name')]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]" - }, - "privateDnsZone_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/privateDnsZones/{0}', parameters('name'))]", - "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", - "properties": { - "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", - "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" - }, - "dependsOn": [ - "privateDnsZone" - ] - }, - "privateDnsZone_roleAssignments": { - "copy": { - "name": "privateDnsZone_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}', parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones', 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": [ - "privateDnsZone" + "service" ] }, - "privateDnsZone_A": { + "service_backends": { "copy": { - "name": "privateDnsZone_A", - "count": "[length(coalesce(parameters('a'), createArray()))]" + "name": "service_backends", + "count": "[length(parameters('backends'))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-ARecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "name": "[format('{0}-Apim-Backend-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "privateDnsZoneName": { + "apiManagementServiceName": { "value": "[parameters('name')]" }, + "url": { + "value": "[parameters('backends')[copyIndex()].url]" + }, + "description": { + "value": "[tryGet(parameters('backends')[copyIndex()], 'description')]" + }, + "credentials": { + "value": "[tryGet(parameters('backends')[copyIndex()], 'credentials')]" + }, "name": { - "value": "[coalesce(parameters('a'), createArray())[copyIndex()].name]" + "value": "[parameters('backends')[copyIndex()].name]" }, - "aRecords": { - "value": "[tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'aRecords')]" + "protocol": { + "value": "[tryGet(parameters('backends')[copyIndex()], 'protocol')]" }, - "metadata": { - "value": "[tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'metadata')]" + "proxy": { + "value": "[tryGet(parameters('backends')[copyIndex()], 'proxy')]" }, - "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'ttl'), 3600)]" + "resourceId": { + "value": "[tryGet(parameters('backends')[copyIndex()], 'resourceId')]" }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'roleAssignments')]" + "serviceFabricCluster": { + "value": "[tryGet(parameters('backends')[copyIndex()], 'serviceFabricCluster')]" + }, + "title": { + "value": "[tryGet(parameters('backends')[copyIndex()], 'title')]" + }, + "tls": { + "value": "[coalesce(tryGet(parameters('backends')[copyIndex()], 'tls'), createObject('validateCertificateChain', true(), 'validateCertificateName', true()))]" } }, "template": { @@ -56984,206 +69461,136 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "2531120132215940282" + "version": "0.34.1.11899", + "templateHash": "60104329731493736" }, - "name": "Private DNS Zone A record", - "description": "This module deploys a Private DNS Zone A record.", - "owner": "Azure/module-maintainers" - }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } - } - } - }, - "nullable": true - } + "name": "API Management Service Backends", + "description": "This module deploys an API Management Service Backend." }, "parameters": { - "privateDnsZoneName": { + "apiManagementServiceName": { "type": "string", "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + "description": "Conditional. The name of the parent API Management service. Required if the template is used in a standalone deployment." } }, "name": { "type": "string", "metadata": { - "description": "Required. The name of the A record." + "description": "Required. Backend Name." } }, - "aRecords": { - "type": "array", + "credentials": { + "type": "object", "nullable": true, "metadata": { - "description": "Optional. The list of A records in the record set." + "description": "Optional. Backend Credentials Contract Properties." } }, - "metadata": { + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Backend Description." + } + }, + "protocol": { + "type": "string", + "defaultValue": "http", + "metadata": { + "description": "Optional. Backend communication protocol. - http or soap." + } + }, + "proxy": { "type": "object", "nullable": true, "metadata": { - "description": "Optional. The metadata attached to the record set." + "description": "Optional. Backend Proxy Contract Properties." } }, - "ttl": { - "type": "int", - "defaultValue": 3600, + "resourceId": { + "type": "string", + "nullable": true, "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." + "description": "Optional. Management Uri of the Resource in External System. This URL can be the Arm Resource ID of Logic Apps, Function Apps or API Apps." } }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", + "serviceFabricCluster": { + "type": "object", + "nullable": true, "metadata": { - "description": "Optional. Array of role assignments to create." + "description": "Optional. Backend Service Fabric Cluster Properties." } - } - }, - "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)))))]" + }, + "title": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Backend Title." + } + }, + "tls": { + "type": "object", + "defaultValue": { + "validateCertificateChain": false, + "validateCertificateName": false + }, + "metadata": { + "description": "Optional. Backend TLS Properties." + } + }, + "url": { + "type": "string", + "metadata": { + "description": "Required. Runtime URL of the Backend." } - ], - "builtInRoleNames": { - "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", - "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, "resources": { - "privateDnsZone": { + "service": { "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" + "type": "Microsoft.ApiManagement/service", + "apiVersion": "2023-05-01-preview", + "name": "[parameters('apiManagementServiceName')]" }, - "A": { - "type": "Microsoft.Network/privateDnsZones/A", - "apiVersion": "2020-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "backend": { + "type": "Microsoft.ApiManagement/service/backends", + "apiVersion": "2022-08-01", + "name": "[format('{0}/{1}', parameters('apiManagementServiceName'), parameters('name'))]", "properties": { - "aRecords": "[parameters('aRecords')]", - "metadata": "[parameters('metadata')]", - "ttl": "[parameters('ttl')]" + "title": "[parameters('title')]", + "description": "[parameters('description')]", + "resourceId": "[parameters('resourceId')]", + "properties": { + "serviceFabricCluster": "[parameters('serviceFabricCluster')]" + }, + "credentials": "[parameters('credentials')]", + "proxy": "[parameters('proxy')]", + "tls": "[parameters('tls')]", + "url": "[parameters('url')]", + "protocol": "[parameters('protocol')]" } - }, - "A_roleAssignments": { - "copy": { - "name": "A_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/A/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/A', parameters('privateDnsZoneName'), 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": [ - "A" - ] } }, "outputs": { - "name": { + "resourceId": { "type": "string", "metadata": { - "description": "The name of the deployed A record." + "description": "The resource ID of the API management service backend." }, - "value": "[parameters('name')]" + "value": "[resourceId('Microsoft.ApiManagement/service/backends', parameters('apiManagementServiceName'), parameters('name'))]" }, - "resourceId": { + "name": { "type": "string", "metadata": { - "description": "The resource ID of the deployed A record." + "description": "The name of the API management service backend." }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/A', parameters('privateDnsZoneName'), parameters('name'))]" + "value": "[parameters('name')]" }, "resourceGroupName": { "type": "string", "metadata": { - "description": "The resource group of the deployed A record." + "description": "The resource group the API management service backend was deployed into." }, "value": "[resourceGroup().name]" } @@ -57191,249 +69598,133 @@ } }, "dependsOn": [ - "privateDnsZone" + "service" ] }, - "privateDnsZone_AAAA": { + "service_caches": { "copy": { - "name": "privateDnsZone_AAAA", - "count": "[length(coalesce(parameters('aaaa'), createArray()))]" + "name": "service_caches", + "count": "[length(parameters('caches'))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-AAAARecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "privateDnsZoneName": { - "value": "[parameters('name')]" - }, - "name": { - "value": "[coalesce(parameters('aaaa'), createArray())[copyIndex()].name]" - }, - "aaaaRecords": { - "value": "[tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'aaaaRecords')]" - }, - "metadata": { - "value": "[tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'metadata')]" - }, - "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'ttl'), 3600)]" - }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'roleAssignments')]" - } - }, - "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.32.4.45862", - "templateHash": "16709340450244912125" - }, - "name": "Private DNS Zone AAAA record", - "description": "This module deploys a Private DNS Zone AAAA record.", - "owner": "Azure/module-maintainers" - }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } - } - } - }, - "nullable": true - } + "name": "[format('{0}-Apim-Cache-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "apiManagementServiceName": { + "value": "[parameters('name')]" + }, + "description": { + "value": "[tryGet(parameters('caches')[copyIndex()], 'description')]" + }, + "connectionString": { + "value": "[parameters('caches')[copyIndex()].connectionString]" + }, + "name": { + "value": "[parameters('caches')[copyIndex()].name]" + }, + "resourceId": { + "value": "[tryGet(parameters('caches')[copyIndex()], 'resourceId')]" + }, + "useFromLocation": { + "value": "[parameters('caches')[copyIndex()].useFromLocation]" + } + }, + "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.34.1.11899", + "templateHash": "2189305885354046724" + }, + "name": "API Management Service Caches", + "description": "This module deploys an API Management Service Cache." }, "parameters": { - "privateDnsZoneName": { + "apiManagementServiceName": { "type": "string", "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + "description": "Conditional. The name of the parent API Management service. Required if the template is used in a standalone deployment." } }, "name": { "type": "string", "metadata": { - "description": "Required. The name of the AAAA record." + "description": "Required. Identifier of the Cache entity. Cache identifier (should be either 'default' or valid Azure region identifier)." } }, - "aaaaRecords": { - "type": "array", - "nullable": true, + "connectionString": { + "type": "string", "metadata": { - "description": "Optional. The list of AAAA records in the record set." + "description": "Required. Runtime connection string to cache. Can be referenced by a named value like so, {{}}." } }, - "metadata": { - "type": "object", + "description": { + "type": "string", "nullable": true, "metadata": { - "description": "Optional. The metadata attached to the record set." + "description": "Optional. Cache description." } }, - "ttl": { - "type": "int", - "defaultValue": 3600, + "resourceId": { + "type": "string", + "nullable": true, "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." + "description": "Optional. Original uri of entity in external system cache points to." } }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", + "useFromLocation": { + "type": "string", "metadata": { - "description": "Optional. Array of role assignments to create." - } - } - }, - "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)))))]" + "description": "Required. Location identifier to use cache from (should be either 'default' or valid Azure region identifier)." } - ], - "builtInRoleNames": { - "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", - "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, "resources": { - "privateDnsZone": { + "service": { "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" + "type": "Microsoft.ApiManagement/service", + "apiVersion": "2023-05-01-preview", + "name": "[parameters('apiManagementServiceName')]" }, - "AAAA": { - "type": "Microsoft.Network/privateDnsZones/AAAA", - "apiVersion": "2020-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "cache": { + "type": "Microsoft.ApiManagement/service/caches", + "apiVersion": "2022-08-01", + "name": "[format('{0}/{1}', parameters('apiManagementServiceName'), parameters('name'))]", "properties": { - "aaaaRecords": "[parameters('aaaaRecords')]", - "metadata": "[parameters('metadata')]", - "ttl": "[parameters('ttl')]" + "description": "[parameters('description')]", + "connectionString": "[parameters('connectionString')]", + "useFromLocation": "[parameters('useFromLocation')]", + "resourceId": "[parameters('resourceId')]" } - }, - "AAAA_roleAssignments": { - "copy": { - "name": "AAAA_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/AAAA/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/AAAA', parameters('privateDnsZoneName'), 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": [ - "AAAA" - ] } }, "outputs": { - "name": { + "resourceId": { "type": "string", "metadata": { - "description": "The name of the deployed AAAA record." + "description": "The resource ID of the API management service cache." }, - "value": "[parameters('name')]" + "value": "[resourceId('Microsoft.ApiManagement/service/caches', parameters('apiManagementServiceName'), parameters('name'))]" }, - "resourceId": { + "name": { "type": "string", "metadata": { - "description": "The resource ID of the deployed AAAA record." + "description": "The name of the API management service cache." }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/AAAA', parameters('privateDnsZoneName'), parameters('name'))]" + "value": "[parameters('name')]" }, "resourceGroupName": { "type": "string", "metadata": { - "description": "The resource group of the deployed AAAA record." + "description": "The resource group the API management service cache was deployed into." }, "value": "[resourceGroup().name]" } @@ -57441,249 +69732,225 @@ } }, "dependsOn": [ - "privateDnsZone" + "service" ] }, - "privateDnsZone_CNAME": { + "service_apiDiagnostics": { "copy": { - "name": "privateDnsZone_CNAME", - "count": "[length(coalesce(parameters('cname'), createArray()))]" + "name": "service_apiDiagnostics", + "count": "[length(parameters('apiDiagnostics'))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-CNAMERecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "name": "[format('{0}-Apim-Api-Diagnostic-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "privateDnsZoneName": { + "apiManagementServiceName": { "value": "[parameters('name')]" }, + "apiName": { + "value": "[parameters('apiDiagnostics')[copyIndex()].apiName]" + }, + "loggerName": { + "value": "[tryGet(parameters('apiDiagnostics')[copyIndex()], 'loggerName')]" + }, "name": { - "value": "[coalesce(parameters('cname'), createArray())[copyIndex()].name]" + "value": "[tryGet(parameters('apiDiagnostics')[copyIndex()], 'name')]" }, - "cnameRecord": { - "value": "[tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'cnameRecord')]" + "alwaysLog": { + "value": "[tryGet(parameters('apiDiagnostics')[copyIndex()], 'alwaysLog')]" }, - "metadata": { - "value": "[tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'metadata')]" + "backend": { + "value": "[tryGet(parameters('apiDiagnostics')[copyIndex()], 'backend')]" }, - "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'ttl'), 3600)]" + "frontend": { + "value": "[tryGet(parameters('apiDiagnostics')[copyIndex()], 'frontend')]" }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'roleAssignments')]" + "httpCorrelationProtocol": { + "value": "[tryGet(parameters('apiDiagnostics')[copyIndex()], 'httpCorrelationProtocol')]" + }, + "logClientIp": { + "value": "[tryGet(parameters('apiDiagnostics')[copyIndex()], 'logClientIp')]" + }, + "metrics": { + "value": "[tryGet(parameters('apiDiagnostics')[copyIndex()], 'metrics')]" + }, + "operationNameFormat": { + "value": "[tryGet(parameters('apiDiagnostics')[copyIndex()], 'operationNameFormat')]" + }, + "samplingPercentage": { + "value": "[tryGet(parameters('apiDiagnostics')[copyIndex()], 'samplingPercentage')]" + }, + "verbosity": { + "value": "[tryGet(parameters('apiDiagnostics')[copyIndex()], 'verbosity')]" } }, "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.32.4.45862", - "templateHash": "9976020649752073181" + "version": "0.34.1.11899", + "templateHash": "220134378763209880" }, - "name": "Private DNS Zone CNAME record", - "description": "This module deploys a Private DNS Zone CNAME record.", - "owner": "Azure/module-maintainers" - }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } - } - } - }, - "nullable": true - } + "name": "API Management Service APIs Diagnostics.", + "description": "This module deploys an API Management Service API Diagnostics." }, "parameters": { - "privateDnsZoneName": { + "apiManagementServiceName": { "type": "string", "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + "description": "Required. The name of the parent API Management service." + } + }, + "apiName": { + "type": "string", + "metadata": { + "description": "Required. The name of the parent API." + } + }, + "loggerName": { + "type": "string", + "metadata": { + "description": "Required. The name of the logger." } }, "name": { "type": "string", + "defaultValue": "local", + "allowedValues": [ + "azuremonitor", + "applicationinsights", + "local" + ], + "metadata": { + "description": "Optional. Type of diagnostic resource." + } + }, + "alwaysLog": { + "type": "string", + "defaultValue": "allErrors", "metadata": { - "description": "Required. The name of the CNAME record." + "description": "Optional. Specifies for what type of messages sampling settings should not apply." } }, - "cnameRecord": { + "backend": { "type": "object", - "nullable": true, + "defaultValue": {}, "metadata": { - "description": "Optional. A CNAME record." + "description": "Optional. Diagnostic settings for incoming/outgoing HTTP messages to the Backend." } }, - "metadata": { + "frontend": { "type": "object", - "nullable": true, + "defaultValue": {}, "metadata": { - "description": "Optional. The metadata attached to the record set." + "description": "Optional. Diagnostic settings for incoming/outgoing HTTP messages to the Gateway." } }, - "ttl": { - "type": "int", - "defaultValue": 3600, + "httpCorrelationProtocol": { + "type": "string", + "defaultValue": "Legacy", + "allowedValues": [ + "Legacy", + "None", + "W3C" + ], "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." + "description": "Conditional. Sets correlation protocol to use for Application Insights diagnostics. Required if using Application Insights." } }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", + "logClientIp": { + "type": "bool", + "defaultValue": false, "metadata": { - "description": "Optional. Array of role assignments to create." + "description": "Optional. Log the ClientIP." } - } - }, - "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)))))]" + }, + "metrics": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Conditional. Emit custom metrics via emit-metric policy. Required if using Application Insights." } - ], - "builtInRoleNames": { - "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", - "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" - } - }, - "resources": { - "privateDnsZone": { - "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" }, - "CNAME": { - "type": "Microsoft.Network/privateDnsZones/CNAME", - "apiVersion": "2020-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "properties": { - "cnameRecord": "[parameters('cnameRecord')]", - "metadata": "[parameters('metadata')]", - "ttl": "[parameters('ttl')]" + "operationNameFormat": { + "type": "string", + "defaultValue": "Name", + "allowedValues": [ + "Name", + "URI" + ], + "metadata": { + "description": "Conditional. The format of the Operation Name for Application Insights telemetries. Required if using Application Insights." } }, - "CNAME_roleAssignments": { - "copy": { - "name": "CNAME_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/CNAME/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/CNAME', parameters('privateDnsZoneName'), 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": [ - "CNAME" - ] + "samplingPercentage": { + "type": "int", + "defaultValue": 100, + "metadata": { + "description": "Optional. Rate of sampling for fixed-rate sampling. Specifies the percentage of requests that are logged. 0% sampling means zero requests logged, while 100% sampling means all requests logged." + } + }, + "verbosity": { + "type": "string", + "defaultValue": "error", + "allowedValues": [ + "error", + "information", + "verbose" + ], + "metadata": { + "description": "Optional. The verbosity level applied to traces emitted by trace policies." + } } }, + "resources": [ + { + "type": "Microsoft.ApiManagement/service/apis/diagnostics", + "apiVersion": "2022-08-01", + "name": "[format('{0}/{1}/{2}', parameters('apiManagementServiceName'), parameters('apiName'), parameters('name'))]", + "properties": { + "alwaysLog": "[parameters('alwaysLog')]", + "backend": "[parameters('backend')]", + "frontend": "[parameters('frontend')]", + "httpCorrelationProtocol": "[parameters('httpCorrelationProtocol')]", + "logClientIp": "[parameters('logClientIp')]", + "loggerId": "[resourceId('Microsoft.ApiManagement/service/loggers', parameters('apiManagementServiceName'), parameters('loggerName'))]", + "metrics": "[parameters('metrics')]", + "operationNameFormat": "[parameters('operationNameFormat')]", + "sampling": { + "percentage": "[parameters('samplingPercentage')]", + "samplingType": "fixed" + }, + "verbosity": "[parameters('verbosity')]" + } + } + ], "outputs": { - "name": { + "resourceId": { "type": "string", "metadata": { - "description": "The name of the deployed CNAME record." + "description": "The resource ID of the API diagnostic." }, - "value": "[parameters('name')]" + "value": "[resourceId('Microsoft.ApiManagement/service/apis/diagnostics', parameters('apiManagementServiceName'), parameters('apiName'), parameters('name'))]" }, - "resourceId": { + "name": { "type": "string", "metadata": { - "description": "The resource ID of the deployed CNAME record." + "description": "The name of the API diagnostic." }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/CNAME', parameters('privateDnsZoneName'), parameters('name'))]" + "value": "[parameters('name')]" }, "resourceGroupName": { "type": "string", "metadata": { - "description": "The resource group of the deployed CNAME record." + "description": "The resource group the API diagnostic was deployed into." }, "value": "[resourceGroup().name]" } @@ -57691,40 +69958,63 @@ } }, "dependsOn": [ - "privateDnsZone" + "service", + "service_apis", + "service_loggers" ] }, - "privateDnsZone_MX": { + "service_identityProviders": { "copy": { - "name": "privateDnsZone_MX", - "count": "[length(coalesce(parameters('mx'), createArray()))]" + "name": "service_identityProviders", + "count": "[length(parameters('identityProviders'))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-MXRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "name": "[format('{0}-Apim-IdentityProvider-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "privateDnsZoneName": { + "apiManagementServiceName": { "value": "[parameters('name')]" }, "name": { - "value": "[coalesce(parameters('mx'), createArray())[copyIndex()].name]" + "value": "[parameters('identityProviders')[copyIndex()].name]" }, - "metadata": { - "value": "[tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'metadata')]" + "allowedTenants": { + "value": "[coalesce(tryGet(parameters('identityProviders')[copyIndex()], 'allowedTenants'), createArray())]" }, - "mxRecords": { - "value": "[tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'mxRecords')]" + "authority": { + "value": "[coalesce(tryGet(parameters('identityProviders')[copyIndex()], 'authority'), '')]" + }, + "clientId": { + "value": "[coalesce(tryGet(parameters('identityProviders')[copyIndex()], 'clientId'), '')]" + }, + "clientLibrary": { + "value": "[coalesce(tryGet(parameters('identityProviders')[copyIndex()], 'clientLibrary'), '')]" + }, + "clientSecret": { + "value": "[coalesce(tryGet(parameters('identityProviders')[copyIndex()], 'clientSecret'), '')]" + }, + "passwordResetPolicyName": { + "value": "[coalesce(tryGet(parameters('identityProviders')[copyIndex()], 'passwordResetPolicyName'), '')]" + }, + "profileEditingPolicyName": { + "value": "[coalesce(tryGet(parameters('identityProviders')[copyIndex()], 'profileEditingPolicyName'), '')]" + }, + "signInPolicyName": { + "value": "[coalesce(tryGet(parameters('identityProviders')[copyIndex()], 'signInPolicyName'), '')]" + }, + "signInTenant": { + "value": "[coalesce(tryGet(parameters('identityProviders')[copyIndex()], 'signInTenant'), '')]" }, - "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'ttl'), 3600)]" + "signUpPolicyName": { + "value": "[coalesce(tryGet(parameters('identityProviders')[copyIndex()], 'signUpPolicyName'), '')]" }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'roleAssignments')]" + "type": { + "value": "[coalesce(tryGet(parameters('identityProviders')[copyIndex()], 'type'), 'aad')]" } }, "template": { @@ -57734,206 +70024,163 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "2520323624213076361" + "version": "0.34.1.11899", + "templateHash": "1181738654147388268" }, - "name": "Private DNS Zone MX record", - "description": "This module deploys a Private DNS Zone MX record.", - "owner": "Azure/module-maintainers" - }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } - } - } - }, - "nullable": true - } + "name": "API Management Service Identity Providers", + "description": "This module deploys an API Management Service Identity Provider." }, "parameters": { - "privateDnsZoneName": { + "apiManagementServiceName": { "type": "string", "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + "description": "Conditional. The name of the parent API Management service. Required if the template is used in a standalone deployment." } }, - "name": { + "allowedTenants": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. List of Allowed Tenants when configuring Azure Active Directory login. - string." + } + }, + "authority": { "type": "string", + "defaultValue": "", "metadata": { - "description": "Required. The name of the MX record." + "description": "Optional. OpenID Connect discovery endpoint hostname for AAD or AAD B2C." } }, - "metadata": { - "type": "object", - "nullable": true, + "clientId": { + "type": "string", + "defaultValue": "", "metadata": { - "description": "Optional. The metadata attached to the record set." + "description": "Conditional. Client ID of the Application in the external Identity Provider. Required if identity provider is used." } }, - "mxRecords": { - "type": "array", + "clientLibrary": { + "type": "string", "nullable": true, + "allowedValues": [ + "ADAL", + "MSAL-2" + ], "metadata": { - "description": "Optional. The list of MX records in the record set." + "description": "Optional. The client library to be used in the developer portal. Only applies to AAD and AAD B2C Identity Provider." } }, - "ttl": { - "type": "int", - "defaultValue": 3600, + "clientSecret": { + "type": "securestring", + "defaultValue": "", "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." + "description": "Conditional. Client secret of the Application in external Identity Provider, used to authenticate login request. Required if identity provider is used." } }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", + "passwordResetPolicyName": { + "type": "string", + "defaultValue": "", "metadata": { - "description": "Optional. Array of role assignments to create." + "description": "Optional. Password Reset Policy Name. Only applies to AAD B2C Identity Provider." + } + }, + "profileEditingPolicyName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Profile Editing Policy Name. Only applies to AAD B2C Identity Provider." + } + }, + "signInPolicyName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Signin Policy Name. Only applies to AAD B2C Identity Provider." + } + }, + "signInTenant": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The TenantId to use instead of Common when logging into Active Directory." + } + }, + "signUpPolicyName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Signup Policy Name. Only applies to AAD B2C Identity Provider." + } + }, + "type": { + "type": "string", + "defaultValue": "aad", + "allowedValues": [ + "aad", + "aadB2C", + "facebook", + "google", + "microsoft", + "twitter" + ], + "metadata": { + "description": "Optional. Identity Provider Type identifier." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. Identity provider name." } } }, "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')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", - "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" - } + "isAadB2C": "[equals(parameters('type'), 'aadB2C')]" }, "resources": { - "privateDnsZone": { + "service": { "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" + "type": "Microsoft.ApiManagement/service", + "apiVersion": "2023-05-01-preview", + "name": "[parameters('apiManagementServiceName')]" }, - "MX": { - "type": "Microsoft.Network/privateDnsZones/MX", - "apiVersion": "2020-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "identityProvider": { + "type": "Microsoft.ApiManagement/service/identityProviders", + "apiVersion": "2022-08-01", + "name": "[format('{0}/{1}', parameters('apiManagementServiceName'), parameters('name'))]", "properties": { - "metadata": "[parameters('metadata')]", - "mxRecords": "[parameters('mxRecords')]", - "ttl": "[parameters('ttl')]" + "type": "[parameters('type')]", + "signinTenant": "[parameters('signInTenant')]", + "allowedTenants": "[parameters('allowedTenants')]", + "authority": "[parameters('authority')]", + "signupPolicyName": "[if(variables('isAadB2C'), parameters('signUpPolicyName'), null())]", + "signinPolicyName": "[if(variables('isAadB2C'), parameters('signInPolicyName'), null())]", + "profileEditingPolicyName": "[if(variables('isAadB2C'), parameters('profileEditingPolicyName'), null())]", + "passwordResetPolicyName": "[if(variables('isAadB2C'), parameters('passwordResetPolicyName'), null())]", + "clientId": "[parameters('clientId')]", + "clientLibrary": "[parameters('clientLibrary')]", + "clientSecret": "[parameters('clientSecret')]" } - }, - "MX_roleAssignments": { - "copy": { - "name": "MX_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/MX/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/MX', parameters('privateDnsZoneName'), 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": [ - "MX" - ] } }, "outputs": { - "name": { + "resourceId": { "type": "string", "metadata": { - "description": "The name of the deployed MX record." + "description": "The resource ID of the API management service identity provider." }, - "value": "[parameters('name')]" + "value": "[resourceId('Microsoft.ApiManagement/service/identityProviders', parameters('apiManagementServiceName'), parameters('name'))]" }, - "resourceId": { + "name": { "type": "string", "metadata": { - "description": "The resource ID of the deployed MX record." + "description": "The name of the API management service identity provider." }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/MX', parameters('privateDnsZoneName'), parameters('name'))]" + "value": "[parameters('name')]" }, "resourceGroupName": { "type": "string", "metadata": { - "description": "The resource group of the deployed MX record." + "description": "The resource group the API management service identity provider was deployed into." }, "value": "[resourceGroup().name]" } @@ -57941,249 +70188,141 @@ } }, "dependsOn": [ - "privateDnsZone" + "service" ] }, - "privateDnsZone_PTR": { + "service_loggers": { "copy": { - "name": "privateDnsZone_PTR", - "count": "[length(coalesce(parameters('ptr'), createArray()))]" + "name": "service_loggers", + "count": "[length(parameters('loggers'))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-PTRRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "name": "[format('{0}-Apim-Logger-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "privateDnsZoneName": { + "name": { + "value": "[parameters('loggers')[copyIndex()].name]" + }, + "apiManagementServiceName": { "value": "[parameters('name')]" }, - "name": { - "value": "[coalesce(parameters('ptr'), createArray())[copyIndex()].name]" + "credentials": { + "value": "[coalesce(tryGet(parameters('loggers')[copyIndex()], 'credentials'), createObject())]" }, - "metadata": { - "value": "[tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'metadata')]" + "isBuffered": { + "value": "[tryGet(parameters('loggers')[copyIndex()], 'isBuffered')]" }, - "ptrRecords": { - "value": "[tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'ptrRecords')]" + "description": { + "value": "[tryGet(parameters('loggers')[copyIndex()], 'loggerDescription')]" }, - "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'ttl'), 3600)]" + "type": { + "value": "[coalesce(tryGet(parameters('loggers')[copyIndex()], 'loggerType'), 'azureMonitor')]" }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'roleAssignments')]" + "targetResourceId": { + "value": "[coalesce(tryGet(parameters('loggers')[copyIndex()], 'targetResourceId'), '')]" } }, "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.32.4.45862", - "templateHash": "3080404733048745471" + "version": "0.34.1.11899", + "templateHash": "13484364421614720756" }, - "name": "Private DNS Zone PTR record", - "description": "This module deploys a Private DNS Zone PTR record.", - "owner": "Azure/module-maintainers" - }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } - } - } - }, - "nullable": true - } + "name": "API Management Service Loggers", + "description": "This module deploys an API Management Service Logger." }, "parameters": { - "privateDnsZoneName": { + "apiManagementServiceName": { "type": "string", "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + "description": "Conditional. The name of the parent API Management service. Required if the template is used in a standalone deployment." } }, "name": { "type": "string", "metadata": { - "description": "Required. The name of the PTR record." + "description": "Required. Resource Name." } }, - "metadata": { - "type": "object", - "nullable": true, + "description": { + "type": "string", + "defaultValue": "", "metadata": { - "description": "Optional. The metadata attached to the record set." + "description": "Optional. Logger description." } }, - "ptrRecords": { - "type": "array", - "nullable": true, + "isBuffered": { + "type": "bool", + "defaultValue": true, "metadata": { - "description": "Optional. The list of PTR records in the record set." + "description": "Optional. Whether records are buffered in the logger before publishing." } }, - "ttl": { - "type": "int", - "defaultValue": 3600, + "type": { + "type": "string", + "allowedValues": [ + "applicationInsights", + "azureEventHub", + "azureMonitor" + ], "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." + "description": "Required. Logger type." } }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", + "targetResourceId": { + "type": "string", "metadata": { - "description": "Optional. Array of role assignments to create." + "description": "Conditional. Required if loggerType = applicationInsights or azureEventHub. Azure Resource Id of a log target (either Azure Event Hub resource or Azure Application Insights resource)." } - } - }, - "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)))))]" + }, + "credentials": { + "type": "secureObject", + "metadata": { + "description": "Conditional. Required if loggerType = applicationInsights or azureEventHub. The name and SendRule connection string of the event hub for azureEventHub logger. Instrumentation key for applicationInsights logger." } - ], - "builtInRoleNames": { - "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", - "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, - "resources": { - "privateDnsZone": { - "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" - }, - "PTR": { - "type": "Microsoft.Network/privateDnsZones/PTR", - "apiVersion": "2020-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "resources": [ + { + "type": "Microsoft.ApiManagement/service/loggers", + "apiVersion": "2022-08-01", + "name": "[format('{0}/{1}', parameters('apiManagementServiceName'), parameters('name'))]", "properties": { - "metadata": "[parameters('metadata')]", - "ptrRecords": "[parameters('ptrRecords')]", - "ttl": "[parameters('ttl')]" + "credentials": "[parameters('credentials')]", + "description": "[parameters('description')]", + "isBuffered": "[parameters('isBuffered')]", + "loggerType": "[parameters('type')]", + "resourceId": "[parameters('targetResourceId')]" } - }, - "PTR_roleAssignments": { - "copy": { - "name": "PTR_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/PTR/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/PTR', parameters('privateDnsZoneName'), 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": [ - "PTR" - ] } - }, + ], "outputs": { - "name": { + "resourceId": { "type": "string", "metadata": { - "description": "The name of the deployed PTR record." + "description": "The resource ID of the logger." }, - "value": "[parameters('name')]" + "value": "[resourceId('Microsoft.ApiManagement/service/loggers', parameters('apiManagementServiceName'), parameters('name'))]" }, - "resourceId": { + "name": { "type": "string", "metadata": { - "description": "The resource ID of the deployed PTR record." + "description": "The name of the logger." }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/PTR', parameters('privateDnsZoneName'), parameters('name'))]" + "value": "[parameters('name')]" }, "resourceGroupName": { "type": "string", "metadata": { - "description": "The resource group of the deployed PTR record." + "description": "The resource group the named value was deployed into." }, "value": "[resourceGroup().name]" } @@ -58191,40 +70330,44 @@ } }, "dependsOn": [ - "privateDnsZone" + "service", + "service_namedValues" ] }, - "privateDnsZone_SOA": { + "service_namedValues": { "copy": { - "name": "privateDnsZone_SOA", - "count": "[length(coalesce(parameters('soa'), createArray()))]" + "name": "service_namedValues", + "count": "[length(parameters('namedValues'))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-SOARecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "name": "[format('{0}-Apim-NamedValue-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "privateDnsZoneName": { + "apiManagementServiceName": { "value": "[parameters('name')]" }, - "name": { - "value": "[coalesce(parameters('soa'), createArray())[copyIndex()].name]" + "displayName": { + "value": "[parameters('namedValues')[copyIndex()].displayName]" }, - "metadata": { - "value": "[tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'metadata')]" + "keyVault": { + "value": "[coalesce(tryGet(parameters('namedValues')[copyIndex()], 'keyVault'), createObject())]" }, - "soaRecord": { - "value": "[tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'soaRecord')]" + "name": { + "value": "[parameters('namedValues')[copyIndex()].name]" }, - "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'ttl'), 3600)]" + "tags": { + "value": "[tryGet(parameters('namedValues')[copyIndex()], 'tags')]" }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'roleAssignments')]" + "secret": { + "value": "[coalesce(tryGet(parameters('namedValues')[copyIndex()], 'secret'), false())]" + }, + "value": { + "value": "[coalesce(tryGet(parameters('namedValues')[copyIndex()], 'value'), parameters('newGuidValue'))]" } }, "template": { @@ -58234,206 +70377,102 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "6653951445614700931" + "version": "0.34.1.11899", + "templateHash": "17330477206748787798" }, - "name": "Private DNS Zone SOA record", - "description": "This module deploys a Private DNS Zone SOA record.", - "owner": "Azure/module-maintainers" - }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } - } - } - }, - "nullable": true - } + "name": "API Management Service Named Values", + "description": "This module deploys an API Management Service Named Value." }, "parameters": { - "privateDnsZoneName": { + "apiManagementServiceName": { "type": "string", "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + "description": "Conditional. The name of the parent API Management service. Required if the template is used in a standalone deployment." } }, - "name": { + "displayName": { "type": "string", "metadata": { - "description": "Required. The name of the SOA record." + "description": "Required. Unique name of NamedValue. It may contain only letters, digits, period, dash, and underscore characters." } }, - "metadata": { + "keyVault": { "type": "object", - "nullable": true, + "defaultValue": {}, "metadata": { - "description": "Optional. The metadata attached to the record set." + "description": "Optional. KeyVault location details of the namedValue." } }, - "soaRecord": { - "type": "object", + "name": { + "type": "string", + "metadata": { + "description": "Required. Named value Name." + } + }, + "tags": { + "type": "array", "nullable": true, "metadata": { - "description": "Optional. A SOA record." + "description": "Optional. Tags that when provided can be used to filter the NamedValue list. - string." } }, - "ttl": { - "type": "int", - "defaultValue": 3600, + "secret": { + "type": "bool", + "defaultValue": false, "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." + "description": "Optional. Determines whether the value is a secret and should be encrypted or not. Default value is false." } }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", + "value": { + "type": "string", + "defaultValue": "[newGuid()]", "metadata": { - "description": "Optional. Array of role assignments to create." + "description": "Optional. Value of the NamedValue. Can contain policy expressions. It may not be empty or consist only of whitespace. This property will not be filled on 'GET' operations! Use '/listSecrets' POST request to get the value." } } }, "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')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", - "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" - } + "keyVaultEmpty": "[empty(parameters('keyVault'))]" }, "resources": { - "privateDnsZone": { + "service": { "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" + "type": "Microsoft.ApiManagement/service", + "apiVersion": "2023-05-01-preview", + "name": "[parameters('apiManagementServiceName')]" }, - "SOA": { - "type": "Microsoft.Network/privateDnsZones/SOA", - "apiVersion": "2020-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "namedValue": { + "type": "Microsoft.ApiManagement/service/namedValues", + "apiVersion": "2022-08-01", + "name": "[format('{0}/{1}', parameters('apiManagementServiceName'), parameters('name'))]", "properties": { - "metadata": "[parameters('metadata')]", - "soaRecord": "[parameters('soaRecord')]", - "ttl": "[parameters('ttl')]" + "tags": "[parameters('tags')]", + "secret": "[parameters('secret')]", + "displayName": "[parameters('displayName')]", + "value": "[if(variables('keyVaultEmpty'), parameters('value'), null())]", + "keyVault": "[if(not(variables('keyVaultEmpty')), parameters('keyVault'), null())]" } - }, - "SOA_roleAssignments": { - "copy": { - "name": "SOA_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/SOA/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/SOA', parameters('privateDnsZoneName'), 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": [ - "SOA" - ] } }, "outputs": { - "name": { + "resourceId": { "type": "string", "metadata": { - "description": "The name of the deployed SOA record." + "description": "The resource ID of the named value." }, - "value": "[parameters('name')]" + "value": "[resourceId('Microsoft.ApiManagement/service/namedValues', parameters('apiManagementServiceName'), parameters('name'))]" }, - "resourceId": { + "name": { "type": "string", "metadata": { - "description": "The resource ID of the deployed SOA record." + "description": "The name of the named value." }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/SOA', parameters('privateDnsZoneName'), parameters('name'))]" + "value": "[parameters('name')]" }, "resourceGroupName": { "type": "string", "metadata": { - "description": "The resource group of the deployed SOA record." + "description": "The resource group the named value was deployed into." }, "value": "[resourceGroup().name]" } @@ -58441,249 +70480,208 @@ } }, "dependsOn": [ - "privateDnsZone" + "service" ] }, - "privateDnsZone_SRV": { + "service_portalsettings": { "copy": { - "name": "privateDnsZone_SRV", - "count": "[length(coalesce(parameters('srv'), createArray()))]" + "name": "service_portalsettings", + "count": "[length(parameters('portalsettings'))]" }, + "condition": "[not(empty(parameters('portalsettings')[copyIndex()].properties))]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-SRVRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "name": "[format('{0}-Apim-PortalSetting-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "privateDnsZoneName": { + "apiManagementServiceName": { "value": "[parameters('name')]" }, "name": { - "value": "[coalesce(parameters('srv'), createArray())[copyIndex()].name]" - }, - "metadata": { - "value": "[tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'metadata')]" - }, - "srvRecords": { - "value": "[tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'srvRecords')]" - }, - "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'ttl'), 3600)]" + "value": "[parameters('portalsettings')[copyIndex()].name]" }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'roleAssignments')]" + "properties": { + "value": "[parameters('portalsettings')[copyIndex()].properties]" } }, "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.32.4.45862", - "templateHash": "5790774778713328446" + "version": "0.34.1.11899", + "templateHash": "11836027592515216348" }, - "name": "Private DNS Zone SRV record", - "description": "This module deploys a Private DNS Zone SRV record.", - "owner": "Azure/module-maintainers" - }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } - } - } - }, - "nullable": true - } + "name": "API Management Service Portal Settings", + "description": "This module deploys an API Management Service Portal Setting." }, "parameters": { - "privateDnsZoneName": { + "apiManagementServiceName": { "type": "string", "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + "description": "Conditional. The name of the parent API Management service. Required if the template is used in a standalone deployment." } }, "name": { "type": "string", + "allowedValues": [ + "delegation", + "signin", + "signup" + ], "metadata": { - "description": "Required. The name of the SRV record." + "description": "Required. Portal setting name." } }, - "metadata": { + "properties": { "type": "object", - "nullable": true, "metadata": { - "description": "Optional. The metadata attached to the record set." + "description": "Required. Portal setting properties." } + } + }, + "resources": [ + { + "type": "Microsoft.ApiManagement/service/portalsettings", + "apiVersion": "2022-08-01", + "name": "[format('{0}/{1}', parameters('apiManagementServiceName'), parameters('name'))]", + "properties": "[parameters('properties')]" + } + ], + "outputs": { + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the API management service portal setting." + }, + "value": "[resourceId('Microsoft.ApiManagement/service/portalsettings', parameters('apiManagementServiceName'), parameters('name'))]" }, - "srvRecords": { - "type": "array", - "nullable": true, + "name": { + "type": "string", "metadata": { - "description": "Optional. The list of SRV records in the record set." + "description": "The name of the API management service portal setting." + }, + "value": "[parameters('name')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the API management service portal setting was deployed into." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "service" + ] + }, + "service_policies": { + "copy": { + "name": "service_policies", + "count": "[length(parameters('policies'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Apim-Policy-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "apiManagementServiceName": { + "value": "[parameters('name')]" + }, + "value": { + "value": "[parameters('policies')[copyIndex()].value]" + }, + "format": { + "value": "[coalesce(tryGet(parameters('policies')[copyIndex()], 'format'), 'xml')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.34.1.11899", + "templateHash": "11662265993484377181" + }, + "name": "API Management Service Policies", + "description": "This module deploys an API Management Service Policy." + }, + "parameters": { + "apiManagementServiceName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent API Management service. Required if the template is used in a standalone deployment." } }, - "ttl": { - "type": "int", - "defaultValue": 3600, + "name": { + "type": "string", + "defaultValue": "policy", "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." + "description": "Optional. The name of the policy." } }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", + "format": { + "type": "string", + "defaultValue": "xml", + "allowedValues": [ + "rawxml", + "rawxml-link", + "xml", + "xml-link" + ], "metadata": { - "description": "Optional. Array of role assignments to create." + "description": "Optional. Format of the policyContent." } - } - }, - "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)))))]" + }, + "value": { + "type": "string", + "metadata": { + "description": "Required. Contents of the Policy as defined by the format." } - ], - "builtInRoleNames": { - "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", - "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, - "resources": { - "privateDnsZone": { - "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" - }, - "SRV": { - "type": "Microsoft.Network/privateDnsZones/SRV", - "apiVersion": "2020-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "resources": [ + { + "type": "Microsoft.ApiManagement/service/policies", + "apiVersion": "2022-08-01", + "name": "[format('{0}/{1}', parameters('apiManagementServiceName'), parameters('name'))]", "properties": { - "metadata": "[parameters('metadata')]", - "srvRecords": "[parameters('srvRecords')]", - "ttl": "[parameters('ttl')]" + "format": "[parameters('format')]", + "value": "[parameters('value')]" } - }, - "SRV_roleAssignments": { - "copy": { - "name": "SRV_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/SRV/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/SRV', parameters('privateDnsZoneName'), 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": [ - "SRV" - ] } - }, + ], "outputs": { - "name": { + "resourceId": { "type": "string", "metadata": { - "description": "The name of the deployed SRV record." + "description": "The resource ID of the API management service policy." }, - "value": "[parameters('name')]" + "value": "[resourceId('Microsoft.ApiManagement/service/policies', parameters('apiManagementServiceName'), parameters('name'))]" }, - "resourceId": { + "name": { "type": "string", "metadata": { - "description": "The resource ID of the deployed SRV record." + "description": "The name of the API management service policy." }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/SRV', parameters('privateDnsZoneName'), parameters('name'))]" + "value": "[parameters('name')]" }, "resourceGroupName": { "type": "string", "metadata": { - "description": "The resource group of the deployed SRV record." + "description": "The resource group the API management service policy was deployed into." }, "value": "[resourceGroup().name]" } @@ -58691,293 +70689,430 @@ } }, "dependsOn": [ - "privateDnsZone" + "service" ] }, - "privateDnsZone_TXT": { + "service_products": { "copy": { - "name": "privateDnsZone_TXT", - "count": "[length(coalesce(parameters('txt'), createArray()))]" + "name": "service_products", + "count": "[length(parameters('products'))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-TXTRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "name": "[format('{0}-Apim-Product-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "privateDnsZoneName": { + "displayName": { + "value": "[parameters('products')[copyIndex()].displayName]" + }, + "apiManagementServiceName": { "value": "[parameters('name')]" }, + "apis": { + "value": "[coalesce(tryGet(parameters('products')[copyIndex()], 'apis'), createArray())]" + }, + "approvalRequired": { + "value": "[coalesce(tryGet(parameters('products')[copyIndex()], 'approvalRequired'), false())]" + }, + "groups": { + "value": "[coalesce(tryGet(parameters('products')[copyIndex()], 'groups'), createArray())]" + }, "name": { - "value": "[coalesce(parameters('txt'), createArray())[copyIndex()].name]" + "value": "[parameters('products')[copyIndex()].name]" }, - "metadata": { - "value": "[tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'metadata')]" + "description": { + "value": "[coalesce(tryGet(parameters('products')[copyIndex()], 'description'), '')]" }, - "txtRecords": { - "value": "[tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'txtRecords')]" + "state": { + "value": "[coalesce(tryGet(parameters('products')[copyIndex()], 'state'), 'published')]" }, - "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'ttl'), 3600)]" + "subscriptionRequired": { + "value": "[coalesce(tryGet(parameters('products')[copyIndex()], 'subscriptionRequired'), false())]" }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'roleAssignments')]" + "subscriptionsLimit": { + "value": "[coalesce(tryGet(parameters('products')[copyIndex()], 'subscriptionsLimit'), 1)]" + }, + "terms": { + "value": "[coalesce(tryGet(parameters('products')[copyIndex()], 'terms'), '')]" } }, "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.32.4.45862", - "templateHash": "1855369119498044639" + "version": "0.34.1.11899", + "templateHash": "8113178935422733202" }, - "name": "Private DNS Zone TXT record", - "description": "This module deploys a Private DNS Zone TXT record.", - "owner": "Azure/module-maintainers" - }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } - } - } - }, - "nullable": true - } + "name": "API Management Service Products", + "description": "This module deploys an API Management Service Product." }, "parameters": { - "privateDnsZoneName": { + "apiManagementServiceName": { "type": "string", "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + "description": "Conditional. The name of the parent API Management service. Required if the template is used in a standalone deployment." } }, - "name": { + "displayName": { "type": "string", + "maxLength": 300, "metadata": { - "description": "Required. The name of the TXT record." + "description": "Required. API Management Service Products name. Must be 1 to 300 characters long." } }, - "metadata": { - "type": "object", - "nullable": true, + "approvalRequired": { + "type": "bool", + "defaultValue": false, "metadata": { - "description": "Optional. The metadata attached to the record set." + "description": "Optional. Whether subscription approval is required. If false, new subscriptions will be approved automatically enabling developers to call the products APIs immediately after subscribing. If true, administrators must manually approve the subscription before the developer can any of the products APIs. Can be present only if subscriptionRequired property is present and has a value of false." } }, - "ttl": { - "type": "int", - "defaultValue": 3600, + "description": { + "type": "string", + "defaultValue": "", "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." + "description": "Optional. Product description. May include HTML formatting tags." } }, - "txtRecords": { + "apis": { "type": "array", - "nullable": true, + "defaultValue": [], "metadata": { - "description": "Optional. The list of TXT records in the record set." + "description": "Optional. Array of Product APIs." } }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", + "groups": { + "type": "array", + "defaultValue": [], "metadata": { - "description": "Optional. Array of role assignments to create." + "description": "Optional. Array of Product Groups." } - } - }, - "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)))))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. Product Name." + } + }, + "state": { + "type": "string", + "defaultValue": "published", + "metadata": { + "description": "Optional. whether product is published or not. Published products are discoverable by users of developer portal. Non published products are visible only to administrators. Default state of Product is notPublished. - notPublished or published." + } + }, + "subscriptionRequired": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Whether a product subscription is required for accessing APIs included in this product. If true, the product is referred to as \"protected\" and a valid subscription key is required for a request to an API included in the product to succeed. If false, the product is referred to as \"open\" and requests to an API included in the product can be made without a subscription key. If property is omitted when creating a new product it's value is assumed to be true." + } + }, + "subscriptionsLimit": { + "type": "int", + "defaultValue": 1, + "metadata": { + "description": "Optional. Whether the number of subscriptions a user can have to this product at the same time. Set to null or omit to allow unlimited per user subscriptions. Can be present only if subscriptionRequired property is present and has a value of false." + } + }, + "terms": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Product terms of use. Developers trying to subscribe to the product will be presented and required to accept these terms before they can complete the subscription process." } - ], - "builtInRoleNames": { - "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", - "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, - "resources": { - "privateDnsZone": { - "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" - }, - "TXT": { - "type": "Microsoft.Network/privateDnsZones/TXT", - "apiVersion": "2020-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "resources": [ + { + "type": "Microsoft.ApiManagement/service/products", + "apiVersion": "2022-08-01", + "name": "[format('{0}/{1}', parameters('apiManagementServiceName'), parameters('name'))]", "properties": { - "metadata": "[parameters('metadata')]", - "ttl": "[parameters('ttl')]", - "txtRecords": "[parameters('txtRecords')]" + "description": "[parameters('description')]", + "displayName": "[parameters('displayName')]", + "terms": "[parameters('terms')]", + "subscriptionRequired": "[parameters('subscriptionRequired')]", + "approvalRequired": "[if(parameters('subscriptionRequired'), parameters('approvalRequired'), null())]", + "subscriptionsLimit": "[if(parameters('subscriptionRequired'), parameters('subscriptionsLimit'), null())]", + "state": "[parameters('state')]" } }, - "TXT_roleAssignments": { + { "copy": { - "name": "TXT_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + "name": "product_apis", + "count": "[length(parameters('apis'))]" }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/TXT/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/TXT', parameters('privateDnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Api-{1}', deployment().name, copyIndex())]", "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')]" + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "apiManagementServiceName": { + "value": "[parameters('apiManagementServiceName')]" + }, + "name": { + "value": "[parameters('apis')[copyIndex()].name]" + }, + "productName": { + "value": "[parameters('name')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.34.1.11899", + "templateHash": "7782324617213267895" + }, + "name": "API Management Service Products APIs", + "description": "This module deploys an API Management Service Product API." + }, + "parameters": { + "apiManagementServiceName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent API Management service. Required if the template is used in a standalone deployment." + } + }, + "productName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Product. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the product API." + } + } + }, + "resources": [ + { + "type": "Microsoft.ApiManagement/service/products/apis", + "apiVersion": "2022-08-01", + "name": "[format('{0}/{1}/{2}', parameters('apiManagementServiceName'), parameters('productName'), parameters('name'))]" + } + ], + "outputs": { + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the product API." + }, + "value": "[resourceId('Microsoft.ApiManagement/service/products/apis', parameters('apiManagementServiceName'), parameters('productName'), parameters('name'))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the product API." + }, + "value": "[parameters('name')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the product API was deployed into." + }, + "value": "[resourceGroup().name]" + } + } + } + } + }, + { + "copy": { + "name": "product_groups", + "count": "[length(parameters('groups'))]" }, - "dependsOn": [ - "TXT" - ] + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Group-{1}', deployment().name, copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "apiManagementServiceName": { + "value": "[parameters('apiManagementServiceName')]" + }, + "name": { + "value": "[parameters('groups')[copyIndex()].name]" + }, + "productName": { + "value": "[parameters('name')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.34.1.11899", + "templateHash": "8814556585327002532" + }, + "name": "API Management Service Products Groups", + "description": "This module deploys an API Management Service Product Group." + }, + "parameters": { + "apiManagementServiceName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent API Management service. Required if the template is used in a standalone deployment." + } + }, + "productName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Product. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the product group." + } + } + }, + "resources": [ + { + "type": "Microsoft.ApiManagement/service/products/groups", + "apiVersion": "2022-08-01", + "name": "[format('{0}/{1}/{2}', parameters('apiManagementServiceName'), parameters('productName'), parameters('name'))]" + } + ], + "outputs": { + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the product group." + }, + "value": "[resourceId('Microsoft.ApiManagement/service/products/groups', parameters('apiManagementServiceName'), parameters('productName'), parameters('name'))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the product group." + }, + "value": "[parameters('name')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the product group was deployed into." + }, + "value": "[resourceGroup().name]" + } + } + } + } } - }, + ], "outputs": { - "name": { + "resourceId": { "type": "string", "metadata": { - "description": "The name of the deployed TXT record." + "description": "The resource ID of the API management service product." }, - "value": "[parameters('name')]" + "value": "[resourceId('Microsoft.ApiManagement/service/products', parameters('apiManagementServiceName'), parameters('name'))]" }, - "resourceId": { + "name": { "type": "string", "metadata": { - "description": "The resource ID of the deployed TXT record." + "description": "The name of the API management service product." }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/TXT', parameters('privateDnsZoneName'), parameters('name'))]" + "value": "[parameters('name')]" }, "resourceGroupName": { "type": "string", "metadata": { - "description": "The resource group of the deployed TXT record." + "description": "The resource group the API management service product was deployed into." }, "value": "[resourceGroup().name]" + }, + "apiResourceIds": { + "type": "array", + "metadata": { + "description": "The Resources IDs of the API management service product APIs." + }, + "copy": { + "count": "[length(range(0, length(parameters('apis'))))]", + "input": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-Api-{1}', deployment().name, range(0, length(parameters('apis')))[copyIndex()])), '2022-09-01').outputs.resourceId.value]" + } + }, + "groupResourceIds": { + "type": "array", + "metadata": { + "description": "The Resources IDs of the API management service product groups." + }, + "copy": { + "count": "[length(range(0, length(parameters('groups'))))]", + "input": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-Group-{1}', deployment().name, range(0, length(parameters('groups')))[copyIndex()])), '2022-09-01').outputs.resourceId.value]" + } } } } }, "dependsOn": [ - "privateDnsZone" + "service", + "service_apis" ] }, - "privateDnsZone_virtualNetworkLinks": { + "service_subscriptions": { "copy": { - "name": "privateDnsZone_virtualNetworkLinks", - "count": "[length(coalesce(parameters('virtualNetworkLinks'), createArray()))]" + "name": "service_subscriptions", + "count": "[length(parameters('subscriptions'))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-VirtualNetworkLink-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "name": "[format('{0}-Apim-Subscription-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "privateDnsZoneName": { + "apiManagementServiceName": { "value": "[parameters('name')]" }, "name": { - "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'name'), format('{0}-vnetlink', last(split(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()].virtualNetworkResourceId, '/'))))]" + "value": "[parameters('subscriptions')[copyIndex()].name]" }, - "virtualNetworkResourceId": { - "value": "[coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()].virtualNetworkResourceId]" + "displayName": { + "value": "[parameters('subscriptions')[copyIndex()].displayName]" }, - "location": { - "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'location'), 'global')]" + "allowTracing": { + "value": "[tryGet(parameters('subscriptions')[copyIndex()], 'allowTracing')]" }, - "registrationEnabled": { - "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'registrationEnabled'), false())]" + "ownerId": { + "value": "[tryGet(parameters('subscriptions')[copyIndex()], 'ownerId')]" }, - "tags": { - "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + "primaryKey": { + "value": "[tryGet(parameters('subscriptions')[copyIndex()], 'primaryKey')]" }, - "resolutionPolicy": { - "value": "[tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'resolutionPolicy')]" + "scope": { + "value": "[tryGet(parameters('subscriptions')[copyIndex()], 'scope')]" + }, + "secondaryKey": { + "value": "[tryGet(parameters('subscriptions')[copyIndex()], 'secondaryKey')]" + }, + "state": { + "value": "[tryGet(parameters('subscriptions')[copyIndex()], 'state')]" } }, "template": { @@ -58987,158 +71122,173 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "15326596012552051215" + "version": "0.34.1.11899", + "templateHash": "997401927729053094" }, - "name": "Private DNS Zone Virtual Network Link", - "description": "This module deploys a Private DNS Zone Virtual Network Link.", - "owner": "Azure/module-maintainers" + "name": "API Management Service Subscriptions", + "description": "This module deploys an API Management Service Subscription." }, "parameters": { - "privateDnsZoneName": { + "allowTracing": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Determines whether tracing can be enabled." + } + }, + "displayName": { "type": "string", + "maxLength": 100, "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + "description": "Required. API Management Service Subscriptions name. Must be 1 to 100 characters long." } }, - "name": { + "apiManagementServiceName": { "type": "string", - "defaultValue": "[format('{0}-vnetlink', last(split(parameters('virtualNetworkResourceId'), '/')))]", "metadata": { - "description": "Optional. The name of the virtual network link." + "description": "Conditional. The name of the parent API Management service. Required if the template is used in a standalone deployment." } }, - "location": { + "ownerId": { "type": "string", - "defaultValue": "global", + "nullable": true, "metadata": { - "description": "Optional. The location of the PrivateDNSZone. Should be global." + "description": "Optional. User (user ID path) for whom subscription is being created in form /users/{userId}." } }, - "tags": { - "type": "object", + "primaryKey": { + "type": "string", "nullable": true, "metadata": { - "description": "Optional. Tags of the resource." + "description": "Optional. Primary subscription key. If not specified during request key will be generated automatically." } }, - "registrationEnabled": { - "type": "bool", - "defaultValue": false, + "scope": { + "type": "string", + "defaultValue": "/apis", "metadata": { - "description": "Optional. Is auto-registration of virtual machine records in the virtual network in the Private DNS zone enabled?." + "description": "Optional. Scope type to choose between a product, \"allAPIs\" or a specific API. Scope like \"/products/{productId}\" or \"/apis\" or \"/apis/{apiId}\"." } }, - "virtualNetworkResourceId": { + "secondaryKey": { "type": "string", + "nullable": true, "metadata": { - "description": "Required. Link to another virtual network resource ID." + "description": "Optional. Secondary subscription key. If not specified during request key will be generated automatically." } }, - "resolutionPolicy": { + "state": { "type": "string", "nullable": true, "metadata": { - "description": "Optional. The resolution policy on the virtual network link. Only applicable for virtual network links to privatelink zones, and for A,AAAA,CNAME queries. When set to `NxDomainRedirect`, Azure DNS resolver falls back to public resolution if private dns query resolution results in non-existent domain response. `Default` is configured as the default option." + "description": "Optional. Initial subscription state. If no value is specified, subscription is created with Submitted state. Possible states are \"*\" active \"?\" the subscription is active, \"*\" suspended \"?\" the subscription is blocked, and the subscriber cannot call any APIs of the product, * submitted ? the subscription request has been made by the developer, but has not yet been approved or rejected, * rejected ? the subscription request has been denied by an administrator, * cancelled ? the subscription has been cancelled by the developer or administrator, * expired ? the subscription reached its expiration date and was deactivated. - suspended, active, expired, submitted, rejected, cancelled." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. Subscription name." } } }, "resources": { - "privateDnsZone": { + "service": { "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" + "type": "Microsoft.ApiManagement/service", + "apiVersion": "2023-05-01-preview", + "name": "[parameters('apiManagementServiceName')]" }, - "virtualNetworkLink": { - "type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks", - "apiVersion": "2024-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", + "subscription": { + "type": "Microsoft.ApiManagement/service/subscriptions", + "apiVersion": "2022-08-01", + "name": "[format('{0}/{1}', parameters('apiManagementServiceName'), parameters('name'))]", "properties": { - "registrationEnabled": "[parameters('registrationEnabled')]", - "virtualNetwork": { - "id": "[parameters('virtualNetworkResourceId')]" - }, - "resolutionPolicy": "[parameters('resolutionPolicy')]" + "scope": "[parameters('scope')]", + "displayName": "[parameters('displayName')]", + "ownerId": "[parameters('ownerId')]", + "primaryKey": "[parameters('primaryKey')]", + "secondaryKey": "[parameters('secondaryKey')]", + "state": "[parameters('state')]", + "allowTracing": "[parameters('allowTracing')]" } } }, "outputs": { - "name": { + "resourceId": { "type": "string", "metadata": { - "description": "The name of the deployed virtual network link." + "description": "The resource ID of the API management service subscription." }, - "value": "[parameters('name')]" + "value": "[resourceId('Microsoft.ApiManagement/service/subscriptions', parameters('apiManagementServiceName'), parameters('name'))]" }, - "resourceId": { + "name": { "type": "string", "metadata": { - "description": "The resource ID of the deployed virtual network link." + "description": "The name of the API management service subscription." }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/virtualNetworkLinks', parameters('privateDnsZoneName'), parameters('name'))]" + "value": "[parameters('name')]" }, "resourceGroupName": { "type": "string", "metadata": { - "description": "The resource group of the deployed virtual network link." + "description": "The resource group the API management service subscription was deployed into." }, "value": "[resourceGroup().name]" - }, - "location": { - "type": "string", - "metadata": { - "description": "The location the resource was deployed into." - }, - "value": "[reference('virtualNetworkLink', '2024-06-01', 'full').location]" } } } }, "dependsOn": [ - "privateDnsZone" + "service" ] } }, "outputs": { - "resourceGroupName": { + "name": { "type": "string", "metadata": { - "description": "The resource group the private DNS zone was deployed into." + "description": "The name of the API management service." }, - "value": "[resourceGroup().name]" + "value": "[parameters('name')]" }, - "name": { + "resourceId": { "type": "string", "metadata": { - "description": "The name of the private DNS zone." + "description": "The resource ID of the API management service." }, - "value": "[parameters('name')]" + "value": "[resourceId('Microsoft.ApiManagement/service', parameters('name'))]" }, - "resourceId": { + "resourceGroupName": { "type": "string", "metadata": { - "description": "The resource ID of the private DNS zone." + "description": "The resource group the API management service was deployed into." }, - "value": "[resourceId('Microsoft.Network/privateDnsZones', parameters('name'))]" + "value": "[resourceGroup().name]" + }, + "systemAssignedMIPrincipalId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "The principal ID of the system assigned identity." + }, + "value": "[tryGet(tryGet(reference('service', '2024-05-01', 'full'), 'identity'), 'principalId')]" }, "location": { "type": "string", "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('privateDnsZone', '2020-06-01', 'full').location]" + "value": "[reference('service', '2024-05-01', 'full').location]" } } } } }, - "aiSearch": { + { + "condition": "[parameters('networkIsolation')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[take(format('{0}-search-services-deployment', variables('nameFormatted')), 64)]", + "name": "private-dns-apim-deployment", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -59146,43 +71296,15 @@ "mode": "Incremental", "parameters": { "name": { - "value": "[variables('nameFormatted')]" - }, - "location": { - "value": "[parameters('location')]" - }, - "cmkEnforcement": { - "value": "Disabled" - }, - "managedIdentities": { - "value": { - "systemAssigned": true - } - }, - "publicNetworkAccess": "[if(parameters('networkIsolation'), createObject('value', 'Disabled'), createObject('value', 'Enabled'))]", - "disableLocalAuth": { - "value": true - }, - "sku": { - "value": "standard" - }, - "partitionCount": { - "value": 1 - }, - "replicaCount": { - "value": 3 - }, - "roleAssignments": { - "value": "[parameters('roleAssignments')]" + "value": "privatelink.apim.windows.net" }, - "diagnosticSettings": { + "virtualNetworkLinks": { "value": [ { - "workspaceResourceId": "[parameters('logAnalyticsWorkspaceResourceId')]" + "virtualNetworkResourceId": "[parameters('virtualNetworkResourceId')]" } ] }, - "privateEndpoints": "[if(parameters('networkIsolation'), createObject('value', createArray(createObject('privateDnsZoneGroup', createObject('privateDnsZoneGroupConfigs', createArray(createObject('privateDnsZoneResourceId', reference('privateDnsZone').outputs.resourceId.value))), 'subnetResourceId', parameters('virtualNetworkSubnetResourceId')))), createObject('value', createArray()))]", "tags": { "value": "[parameters('tags')]" } @@ -59194,1039 +71316,1607 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "17193695855897695274" + "version": "0.32.4.45862", + "templateHash": "83178825086050429" }, - "name": "Search Services", - "description": "This module deploys a Search Service." + "name": "Private DNS Zones", + "description": "This module deploys a Private DNS zone.", + "owner": "Azure/module-maintainers" }, "definitions": { - "secretsExportConfigurationType": { - "type": "object", - "properties": { - "keyVaultResourceId": { - "type": "string", - "metadata": { - "description": "Required. The key vault name where to store the API Admin keys generated by the modules." - } - }, - "primaryAdminKeyName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The primaryAdminKey secret name to create." - } - }, - "secondaryAdminKeyName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The secondaryAdminKey secret name to create." - } - } - } - }, - "secretsOutputType": { - "type": "object", - "properties": {}, - "additionalProperties": { - "$ref": "#/definitions/secretSetType", - "metadata": { - "description": "An exported secret's references." - } - } - }, - "authOptionsType": { - "type": "object", - "properties": { - "aadOrApiKey": { - "type": "object", - "properties": { - "aadAuthFailureMode": { - "type": "string", - "allowedValues": [ - "http401WithBearerChallenge", - "http403" - ], - "nullable": true, - "metadata": { - "description": "Optional. Describes what response the data plane API of a search service would send for requests that failed authentication." - } + "roleAssignmentType": { + "type": "array", + "items": { + "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." } }, - "nullable": true, - "metadata": { - "description": "Optional. Indicates that either the API key or an access token from a Microsoft Entra ID tenant can be used for authentication." - } - }, - "apiKeyOnly": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Indicates that only the API key can be used for authentication." + "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": { - "__bicep_export!": true - } + "nullable": true }, - "networkRuleSetType": { + "lockType": { "type": "object", "properties": { - "bypass": { + "name": { "type": "string", - "allowedValues": [ - "AzurePortal", - "None" - ], "nullable": true, "metadata": { - "description": "Optional. Network specific rules that determine how the Azure AI Search service may be reached." + "description": "Optional. Specify the name of lock." } }, - "ipRules": { - "type": "array", - "items": { - "$ref": "#/definitions/ipRuleType" - }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], "nullable": true, "metadata": { - "description": "Optional. A list of IP restriction rules that defines the inbound network(s) with allowing access to the search service endpoint. At the meantime, all other public IP networks are blocked by the firewall. These restriction rules are applied only when the 'publicNetworkAccess' of the search service is 'enabled'; otherwise, traffic over public interface is not allowed even with any public IP rules, and private endpoint connections would be the exclusive access method." + "description": "Optional. Specify the type of lock." } } }, - "metadata": { - "__bicep_export!": true - } + "nullable": true }, - "ipRuleType": { - "type": "object", - "properties": { - "value": { - "type": "string", + "aType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, "metadata": { - "description": "Required. Value corresponding to a single IPv4 address (eg., 123.1.2.3) or an IP range in CIDR format (eg., 123.1.2.3/24) to be allowed." + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "aRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "ipv4Address": { + "type": "string", + "metadata": { + "description": "Required. The IPv4 address of this A record." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of A records in the record set." + } } } }, - "metadata": { - "__bicep_export!": true - } + "nullable": true }, - "_1.privateEndpointCustomDnsConfigType": { - "type": "object", - "properties": { - "fqdn": { - "type": "string", - "nullable": true, + "aaaaType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, "metadata": { - "description": "Optional. FQDN that resolves to private endpoint IP address." + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "aaaaRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "ipv6Address": { + "type": "string", + "metadata": { + "description": "Required. The IPv6 address of this AAAA record." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of AAAA records in the record set." + } } - }, - "ipAddresses": { - "type": "array", - "items": { - "type": "string" + } + }, + "nullable": true + }, + "cnameType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } }, "metadata": { - "description": "Required. A list of private IP addresses of the private endpoint." + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "cnameRecord": { + "type": "object", + "properties": { + "cname": { + "type": "string", + "metadata": { + "description": "Required. The canonical name of the CNAME record." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The CNAME record in the record set." + } } } }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.3.0" - } - } + "nullable": true }, - "_1.privateEndpointIpConfigurationType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the resource that is unique within a resource group." - } - }, + "mxType": { + "type": "array", + "items": { + "type": "object", "properties": { - "type": "object", - "properties": { - "groupId": { - "type": "string", - "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." - } - }, - "memberName": { - "type": "string", - "metadata": { - "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to." - } - }, - "privateIPAddress": { - "type": "string", - "metadata": { - "description": "Required. A private IP address obtained from the private endpoint's subnet." - } + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." } }, "metadata": { - "description": "Required. Properties of private endpoint IP configurations." + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "mxRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "exchange": { + "type": "string", + "metadata": { + "description": "Required. The domain name of the mail host for this MX record." + } + }, + "preference": { + "type": "int", + "metadata": { + "description": "Required. The preference value for this MX record." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of MX records in the record set." + } } } }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.3.0" - } - } + "nullable": true }, - "_1.privateEndpointPrivateDnsZoneGroupType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, + "ptrType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, "metadata": { - "description": "Optional. The name of the Private DNS Zone Group." - } - }, - "privateDnsZoneGroupConfigs": { - "type": "array", - "items": { "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." + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "ptrRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "ptrdname": { + "type": "string", + "metadata": { + "description": "Required. The PTR target domain name for this PTR record." + } } } + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of PTR records in the record set." } - }, - "metadata": { - "description": "Required. The private DNS Zone Groups to associate the Private Endpoint. A DNS Zone Group can support up to 5 DNS zones." } } }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.3.0" - } - } + "nullable": true }, - "diagnosticSettingFullType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, + "soaType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, "metadata": { - "description": "Optional. The name of the diagnostic setting." - } - }, - "logCategoriesAndGroups": { - "type": "array", - "items": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "soaRecord": { "type": "object", "properties": { - "category": { + "email": { "type": "string", - "nullable": true, "metadata": { - "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + "description": "Required. The email contact for this SOA record." } }, - "categoryGroup": { + "expireTime": { + "type": "int", + "metadata": { + "description": "Required. The expire time for this SOA record." + } + }, + "host": { "type": "string", - "nullable": true, "metadata": { - "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + "description": "Required. The domain name of the authoritative name server for this SOA record." } }, - "enabled": { - "type": "bool", - "nullable": true, + "minimumTtl": { + "type": "int", "metadata": { - "description": "Optional. Enable or disable the category explicitly. Default is `true`." + "description": "Required. The minimum value for this SOA record. By convention this is used to determine the negative caching duration." } - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." - } - }, - "metricCategories": { - "type": "array", - "items": { - "type": "object", - "properties": { - "category": { - "type": "string", + }, + "refreshTime": { + "type": "int", "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." + "description": "Required. The refresh value for this SOA record." } }, - "enabled": { - "type": "bool", - "nullable": true, + "retryTime": { + "type": "int", "metadata": { - "description": "Optional. Enable or disable the category explicitly. Default is `true`." + "description": "Required. The retry time for this SOA record." + } + }, + "serialNumber": { + "type": "int", + "metadata": { + "description": "Required. The serial number for this SOA record." } } + }, + "nullable": true, + "metadata": { + "description": "Optional. The SOA record in the record set." } - }, - "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, + } + }, + "nullable": true + }, + "srvType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, "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." + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "srvRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "priority": { + "type": "int", + "metadata": { + "description": "Required. The priority value for this SRV record." + } + }, + "weight": { + "type": "int", + "metadata": { + "description": "Required. The weight value for this SRV record." + } + }, + "port": { + "type": "int", + "metadata": { + "description": "Required. The port value for this SRV record." + } + }, + "target": { + "type": "string", + "metadata": { + "description": "Required. The target domain name for this SRV record." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of SRV records in the record set." + } } - }, - "eventHubName": { - "type": "string", - "nullable": true, + } + }, + "nullable": true + }, + "txtType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, "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." + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "txtRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "value": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The text value of this TXT record." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of TXT records in the record set." + } } - }, - "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." + } + }, + "nullable": true + }, + "virtualNetworkLinkType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "minLength": 1, + "maxLength": 80, + "metadata": { + "description": "Optional. The resource name." + } + }, + "virtualNetworkResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the virtual network to link." + } + }, + "location": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Azure Region where the resource lives." + } + }, + "registrationEnabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Is auto-registration of virtual machine records in the virtual network in the Private DNS zone enabled?." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Resource tags." + } + }, + "resolutionPolicy": { + "type": "string", + "allowedValues": [ + "Default", + "NxDomainRedirect" + ], + "nullable": true, + "metadata": { + "description": "Optional. The resolution type of the private-dns-zone fallback machanism." + } } } }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", "metadata": { - "description": "An AVM-aligned type for a diagnostic setting. To be used if both logs & metrics are supported by the resource provider.", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.3.0" - } + "description": "Required. Private DNS zone name." } }, - "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." - } - } - }, + "a": { + "$ref": "#/definitions/aType", "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.3.0" - } + "description": "Optional. Array of A records." } }, - "managedIdentityAllType": { + "aaaa": { + "$ref": "#/definitions/aaaaType", + "metadata": { + "description": "Optional. Array of AAAA records." + } + }, + "cname": { + "$ref": "#/definitions/cnameType", + "metadata": { + "description": "Optional. Array of CNAME records." + } + }, + "mx": { + "$ref": "#/definitions/mxType", + "metadata": { + "description": "Optional. Array of MX records." + } + }, + "ptr": { + "$ref": "#/definitions/ptrType", + "metadata": { + "description": "Optional. Array of PTR records." + } + }, + "soa": { + "$ref": "#/definitions/soaType", + "metadata": { + "description": "Optional. Array of SOA records." + } + }, + "srv": { + "$ref": "#/definitions/srvType", + "metadata": { + "description": "Optional. Array of SRV records." + } + }, + "txt": { + "$ref": "#/definitions/txtType", + "metadata": { + "description": "Optional. Array of TXT records." + } + }, + "virtualNetworkLinks": { + "$ref": "#/definitions/virtualNetworkLinkType", + "metadata": { + "description": "Optional. Array of custom objects describing vNet links of the DNS zone. Each object should contain properties 'virtualNetworkResourceId' and 'registrationEnabled'. The 'vnetResourceId' is a resource ID of a vNet to link, 'registrationEnabled' (bool) enables automatic DNS registration in the zone for the linked vNet." + } + }, + "location": { + "type": "string", + "defaultValue": "global", + "metadata": { + "description": "Optional. The location of the PrivateDNSZone. Should be global." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "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')]", + "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": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privatednszone.{0}.{1}', replace('0.7.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { - "systemAssigned": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Enables system assigned managed identity on the resource." - } - }, - "userAssignedResourceIds": { - "type": "array", - "items": { - "type": "string" - }, - "nullable": true, - "metadata": { - "description": "Optional. The resource ID(s) to assign to the resource. Required if a user assigned identity is used for encryption." + "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" + } } } - }, - "metadata": { - "description": "An AVM-aligned type for a managed identity configuration. To be used if both a system-assigned & user-assigned identities are supported by the resource provider.", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.3.0" - } } }, - "privateEndpointSingleServiceType": { - "type": "object", + "privateDnsZone": { + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]" + }, + "privateDnsZone_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/privateDnsZones/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the Private Endpoint." - } - }, - "location": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The location to deploy the Private Endpoint to." - } - }, - "privateLinkServiceConnectionName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the private link connection to create." - } - }, - "service": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The subresource to deploy the Private Endpoint for. For example \"vault\" for a Key Vault Private Endpoint." - } - }, - "subnetResourceId": { - "type": "string", - "metadata": { - "description": "Required. Resource ID of the subnet where the endpoint needs to be created." - } - }, - "privateDnsZoneGroup": { - "$ref": "#/definitions/_1.privateEndpointPrivateDnsZoneGroupType", - "nullable": true, - "metadata": { - "description": "Optional. The private DNS Zone Group to configure for the Private Endpoint." - } - }, - "isManualConnection": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. If Manual Private Link Connection is required." - } - }, - "manualConnectionRequestMessage": { - "type": "string", - "nullable": true, - "maxLength": 140, - "metadata": { - "description": "Optional. A message passed to the owner of the remote resource with the manual connection request." - } + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "privateDnsZone_roleAssignments": { + "copy": { + "name": "privateDnsZone_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones', 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": [ + "privateDnsZone" + ] + }, + "privateDnsZone_A": { + "copy": { + "name": "privateDnsZone_A", + "count": "[length(coalesce(parameters('a'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-ARecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" }, - "customDnsConfigs": { - "type": "array", - "items": { - "$ref": "#/definitions/_1.privateEndpointCustomDnsConfigType" + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" }, - "nullable": true, - "metadata": { - "description": "Optional. Custom DNS configurations." - } - }, - "ipConfigurations": { - "type": "array", - "items": { - "$ref": "#/definitions/_1.privateEndpointIpConfigurationType" + "name": { + "value": "[coalesce(parameters('a'), createArray())[copyIndex()].name]" }, - "nullable": true, - "metadata": { - "description": "Optional. A list of IP configurations of the Private Endpoint. This will be used to map to the first-party Service endpoints." - } - }, - "applicationSecurityGroupResourceIds": { - "type": "array", - "items": { - "type": "string" + "aRecords": { + "value": "[tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'aRecords')]" }, - "nullable": true, - "metadata": { - "description": "Optional. Application security groups in which the Private Endpoint IP configuration is included." - } - }, - "customNetworkInterfaceName": { - "type": "string", - "nullable": true, "metadata": { - "description": "Optional. The custom name of the network interface attached to the Private Endpoint." + "value": "[tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'metadata')]" + }, + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'ttl'), 3600)]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'roleAssignments')]" } }, - "lock": { - "$ref": "#/definitions/lockType", - "nullable": true, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", "metadata": { - "description": "Optional. Specify the type of lock." - } - }, - "roleAssignments": { - "type": "array", - "items": { - "$ref": "#/definitions/roleAssignmentType" + "_generator": { + "name": "bicep", + "version": "0.32.4.45862", + "templateHash": "2531120132215940282" + }, + "name": "Private DNS Zone A record", + "description": "This module deploys a Private DNS Zone A record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "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." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the A record." + } + }, + "aRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of A records in the record set." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "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')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "A": { + "type": "Microsoft.Network/privateDnsZones/A", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "properties": { + "aRecords": "[parameters('aRecords')]", + "metadata": "[parameters('metadata')]", + "ttl": "[parameters('ttl')]" + } + }, + "A_roleAssignments": { + "copy": { + "name": "A_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/A/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/A', parameters('privateDnsZoneName'), 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": [ + "A" + ] + } }, - "nullable": true, - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "tags": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Tags to be applied on all resources/Resource Groups in this deployment." - } - }, - "enableTelemetry": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Enable/Disable usage telemetry for module." - } - }, - "resourceGroupName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Specify if you want to deploy the Private Endpoint into a different Resource Group than the main resource." + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed A record." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed A record." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/A', parameters('privateDnsZoneName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed A record." + }, + "value": "[resourceGroup().name]" + } } } }, - "metadata": { - "description": "An AVM-aligned type for a private endpoint. To be used if the private endpoint's default service / groupId can be assumed (i.e., for services that only have one Private Endpoint type like 'vault' for key vault).", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.3.0" - } - } + "dependsOn": [ + "privateDnsZone" + ] }, - "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." - } - } + "privateDnsZone_AAAA": { + "copy": { + "name": "privateDnsZone_AAAA", + "count": "[length(coalesce(parameters('aaaa'), createArray()))]" }, - "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.3.0" - } - } - }, - "secretSetType": { - "type": "object", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-AAAARecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { - "secretResourceId": { - "type": "string", + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('aaaa'), createArray())[copyIndex()].name]" + }, + "aaaaRecords": { + "value": "[tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'aaaaRecords')]" + }, "metadata": { - "description": "The resourceId of the exported secret." + "value": "[tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'metadata')]" + }, + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'ttl'), 3600)]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'roleAssignments')]" } }, - "secretUri": { - "type": "string", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", "metadata": { - "description": "The secret URI of the exported secret." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "modules/keyVaultExport.bicep" - } - } - } - }, - "parameters": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the Azure Cognitive Search service to create or update. Search service names must only contain lowercase letters, digits or dashes, cannot use dash as the first two or last one characters, cannot contain consecutive dashes, and must be between 2 and 60 characters in length. Search service names must be globally unique since they are part of the service URI (https://.search.windows.net). You cannot change the service name after the service is created." - } - }, - "authOptions": { - "$ref": "#/definitions/authOptionsType", - "nullable": true, - "metadata": { - "description": "Optional. Defines the options for how the data plane API of a Search service authenticates requests. Must remain an empty object {} if 'disableLocalAuth' is set to true." - } - }, - "disableLocalAuth": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Optional. When set to true, calls to the search service will not be permitted to utilize API keys for authentication. This cannot be set to true if 'authOptions' are defined." - } - }, - "enableTelemetry": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Optional. Enable/Disable usage telemetry for module." - } - }, - "cmkEnforcement": { - "type": "string", - "defaultValue": "Unspecified", - "allowedValues": [ - "Disabled", - "Enabled", - "Unspecified" - ], - "metadata": { - "description": "Optional. Describes a policy that determines how resources within the search service are to be encrypted with Customer Managed Keys." - } - }, - "hostingMode": { - "type": "string", - "defaultValue": "default", - "allowedValues": [ - "default", - "highDensity" - ], - "metadata": { - "description": "Optional. Applicable only for the standard3 SKU. You can set this property to enable up to 3 high density partitions that allow up to 1000 indexes, which is much higher than the maximum indexes allowed for any other SKU. For the standard3 SKU, the value is either 'default' or 'highDensity'. For all other SKUs, this value must be 'default'." - } - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]", - "metadata": { - "description": "Optional. Location for all Resources." - } - }, - "lock": { - "$ref": "#/definitions/lockType", - "nullable": true, - "metadata": { - "description": "Optional. The lock settings for all Resources in the solution." - } - }, - "networkRuleSet": { - "$ref": "#/definitions/networkRuleSetType", - "nullable": true, - "metadata": { - "description": "Optional. Network specific rules that determine how the Azure Cognitive Search service may be reached." - } - }, - "partitionCount": { - "type": "int", - "defaultValue": 1, - "minValue": 1, - "maxValue": 12, - "metadata": { - "description": "Optional. The number of partitions in the search service; if specified, it can be 1, 2, 3, 4, 6, or 12. Values greater than 1 are only valid for standard SKUs. For 'standard3' services with hostingMode set to 'highDensity', the allowed values are between 1 and 3." - } - }, - "privateEndpoints": { - "type": "array", - "items": { - "$ref": "#/definitions/privateEndpointSingleServiceType" - }, - "nullable": true, - "metadata": { - "description": "Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible." - } - }, - "sharedPrivateLinkResources": { - "type": "array", - "defaultValue": [], - "metadata": { - "description": "Optional. The sharedPrivateLinkResources to create as part of the search Service." - } - }, - "publicNetworkAccess": { - "type": "string", - "defaultValue": "Enabled", - "allowedValues": [ - "Enabled", - "Disabled" - ], - "metadata": { - "description": "Optional. This value can be set to 'Enabled' to avoid breaking changes on existing customer resources and templates. If set to 'Disabled', traffic over public interface is not allowed, and private endpoint connections would be the exclusive access method." - } - }, - "secretsExportConfiguration": { - "$ref": "#/definitions/secretsExportConfigurationType", - "nullable": true, - "metadata": { - "description": "Optional. Key vault reference and secret settings for the module's secrets export." - } - }, - "replicaCount": { - "type": "int", - "defaultValue": 3, - "minValue": 1, - "maxValue": 12, - "metadata": { - "description": "Optional. The number of replicas in the search service. If specified, it must be a value between 1 and 12 inclusive for standard SKUs or between 1 and 3 inclusive for basic SKU." - } - }, - "roleAssignments": { - "type": "array", - "items": { - "$ref": "#/definitions/roleAssignmentType" + "_generator": { + "name": "bicep", + "version": "0.32.4.45862", + "templateHash": "16709340450244912125" + }, + "name": "Private DNS Zone AAAA record", + "description": "This module deploys a Private DNS Zone AAAA record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "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." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the AAAA record." + } + }, + "aaaaRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of AAAA records in the record set." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "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')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "AAAA": { + "type": "Microsoft.Network/privateDnsZones/AAAA", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "properties": { + "aaaaRecords": "[parameters('aaaaRecords')]", + "metadata": "[parameters('metadata')]", + "ttl": "[parameters('ttl')]" + } + }, + "AAAA_roleAssignments": { + "copy": { + "name": "AAAA_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/AAAA/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/AAAA', parameters('privateDnsZoneName'), 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": [ + "AAAA" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed AAAA record." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed AAAA record." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/AAAA', parameters('privateDnsZoneName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed AAAA record." + }, + "value": "[resourceGroup().name]" + } + } + } }, - "nullable": true, - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "semanticSearch": { - "type": "string", - "nullable": true, - "allowedValues": [ - "disabled", - "free", - "standard" - ], - "metadata": { - "description": "Optional. Sets options that control the availability of semantic search. This configuration is only possible for certain search SKUs in certain locations." - } - }, - "sku": { - "type": "string", - "defaultValue": "standard", - "allowedValues": [ - "basic", - "free", - "standard", - "standard2", - "standard3", - "storage_optimized_l1", - "storage_optimized_l2" - ], - "metadata": { - "description": "Optional. Defines the SKU of an Azure Cognitive Search Service, which determines price tier and capacity limits." - } - }, - "managedIdentities": { - "$ref": "#/definitions/managedIdentityAllType", - "nullable": true, - "metadata": { - "description": "Optional. The managed identity definition for this resource." - } + "dependsOn": [ + "privateDnsZone" + ] }, - "diagnosticSettings": { - "type": "array", - "items": { - "$ref": "#/definitions/diagnosticSettingFullType" + "privateDnsZone_CNAME": { + "copy": { + "name": "privateDnsZone_CNAME", + "count": "[length(coalesce(parameters('cname'), createArray()))]" }, - "nullable": true, - "metadata": { - "description": "Optional. The diagnostic settings of the service." - } - }, - "tags": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Tags to help categorize the resource in the Azure portal." - } - } - }, - "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)))))]" - } - ], - "enableReferencedModulesTelemetry": false, - "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", - "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned,UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', '')), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", - "builtInRoleNames": { - "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "Search Index Data Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8ebe5a00-799e-43f5-93ac-243d3dce84a7')]", - "Search Index Data Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1407120a-92aa-4202-b7e9-c0e197c71c8f')]", - "Search Service Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7ca78c08-252a-4471-8644-bb5ff32d4ba0')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" - } - }, - "resources": { - "avmTelemetry": { - "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", - "apiVersion": "2024-03-01", - "name": "[format('46d3xbcp.res.search-searchservice.{0}.{1}', replace('0.9.2', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-CNAMERecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('cname'), createArray())[copyIndex()].name]" + }, + "cnameRecord": { + "value": "[tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'cnameRecord')]" + }, + "metadata": { + "value": "[tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'metadata')]" + }, + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'ttl'), 3600)]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'roleAssignments')]" + } + }, "template": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", "contentVersion": "1.0.0.0", - "resources": [], - "outputs": { - "telemetry": { - "type": "String", - "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.32.4.45862", + "templateHash": "9976020649752073181" + }, + "name": "Private DNS Zone CNAME record", + "description": "This module deploys a Private DNS Zone CNAME record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "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." + } + } + } + }, + "nullable": true } - } - } - } - }, - "searchService": { - "type": "Microsoft.Search/searchServices", - "apiVersion": "2024-03-01-preview", - "name": "[parameters('name')]", - "location": "[parameters('location')]", - "sku": { - "name": "[parameters('sku')]" - }, - "tags": "[parameters('tags')]", - "identity": "[variables('identity')]", - "properties": { - "authOptions": "[parameters('authOptions')]", - "disableLocalAuth": "[parameters('disableLocalAuth')]", - "encryptionWithCmk": { - "enforcement": "[parameters('cmkEnforcement')]" - }, - "hostingMode": "[parameters('hostingMode')]", - "networkRuleSet": "[parameters('networkRuleSet')]", - "partitionCount": "[parameters('partitionCount')]", - "replicaCount": "[parameters('replicaCount')]", - "publicNetworkAccess": "[toLower(parameters('publicNetworkAccess'))]", - "semanticSearch": "[parameters('semanticSearch')]" - } - }, - "searchService_diagnosticSettings": { - "copy": { - "name": "searchService_diagnosticSettings", - "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" - }, - "type": "Microsoft.Insights/diagnosticSettings", - "apiVersion": "2021-05-01-preview", - "scope": "[format('Microsoft.Search/searchServices/{0}', parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", - "properties": { - "copy": [ - { - "name": "metrics", - "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", - "input": { - "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", - "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", - "timeGrain": null + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the CNAME record." + } + }, + "cnameRecord": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. A CNAME record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } } }, - { - "name": "logs", - "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", - "input": { - "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", - "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", - "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + "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')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "CNAME": { + "type": "Microsoft.Network/privateDnsZones/CNAME", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "properties": { + "cnameRecord": "[parameters('cnameRecord')]", + "metadata": "[parameters('metadata')]", + "ttl": "[parameters('ttl')]" + } + }, + "CNAME_roleAssignments": { + "copy": { + "name": "CNAME_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/CNAME/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/CNAME', parameters('privateDnsZoneName'), 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": [ + "CNAME" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed CNAME record." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed CNAME record." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/CNAME', parameters('privateDnsZoneName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed CNAME record." + }, + "value": "[resourceGroup().name]" } } - ], - "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", - "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", - "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", - "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", - "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", - "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" - }, - "dependsOn": [ - "searchService" - ] - }, - "searchService_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.Search/searchServices/{0}', parameters('name'))]", - "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", - "properties": { - "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", - "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" - }, - "dependsOn": [ - "searchService" - ] - }, - "searchService_roleAssignments": { - "copy": { - "name": "searchService_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Search/searchServices/{0}', parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Search/searchServices', 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": [ - "searchService" + "privateDnsZone" ] }, - "searchService_privateEndpoints": { + "privateDnsZone_MX": { "copy": { - "name": "searchService_privateEndpoints", - "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]" + "name": "privateDnsZone_MX", + "count": "[length(coalesce(parameters('mx'), createArray()))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-searchService-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "resourceGroup": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupName'), '')]", + "name": "[format('{0}-PrivateDnsZone-MXRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "name": { - "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'name'), format('pep-{0}-{1}-{2}', last(split(resourceId('Microsoft.Search/searchServices', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'searchService'), copyIndex()))]" - }, - "privateLinkServiceConnections": "[if(not(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true())), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.Search/searchServices', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'searchService'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.Search/searchServices', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'searchService')))))), createObject('value', null()))]", - "manualPrivateLinkServiceConnections": "[if(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true()), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.Search/searchServices', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'searchService'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.Search/searchServices', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'searchService')), 'requestMessage', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualConnectionRequestMessage'), 'Manual approval required.'))))), createObject('value', null()))]", - "subnetResourceId": { - "value": "[coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId]" + "privateDnsZoneName": { + "value": "[parameters('name')]" }, - "enableTelemetry": { - "value": "[variables('enableReferencedModulesTelemetry')]" + "name": { + "value": "[coalesce(parameters('mx'), createArray())[copyIndex()].name]" }, - "location": { - "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'location'), reference(split(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location)]" + "metadata": { + "value": "[tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'metadata')]" }, - "lock": { - "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" + "mxRecords": { + "value": "[tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'mxRecords')]" }, - "privateDnsZoneGroup": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroup')]" + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'ttl'), 3600)]" }, "roleAssignments": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'roleAssignments')]" - }, - "tags": { - "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" - }, - "customDnsConfigs": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customDnsConfigs')]" - }, - "ipConfigurations": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'ipConfigurations')]" - }, - "applicationSecurityGroupResourceIds": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'applicationSecurityGroupResourceIds')]" - }, - "customNetworkInterfaceName": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customNetworkInterfaceName')]" + "value": "[tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'roleAssignments')]" } }, "template": { @@ -60236,35 +72926,14 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "1277254088602407590" + "version": "0.32.4.45862", + "templateHash": "2520323624213076361" }, - "name": "Private Endpoints", - "description": "This module deploys a Private Endpoint.", + "name": "Private DNS Zone MX record", + "description": "This module deploys a Private DNS Zone MX record.", "owner": "Azure/module-maintainers" }, "definitions": { - "privateDnsZoneGroupType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the Private DNS Zone Group." - } - }, - "privateDnsZoneGroupConfigs": { - "type": "array", - "items": { - "$ref": "#/definitions/privateDnsZoneGroupConfigType" - }, - "metadata": { - "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." - } - } - } - }, "roleAssignmentType": { "type": "array", "items": { @@ -60337,261 +73006,290 @@ } }, "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } }, - "lockType": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the MX record." + } + }, + "metadata": { "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "mxRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of MX records in the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "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')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "MX": { + "type": "Microsoft.Network/privateDnsZones/MX", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", "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." - } - } + "metadata": "[parameters('metadata')]", + "mxRecords": "[parameters('mxRecords')]", + "ttl": "[parameters('ttl')]" + } + }, + "MX_roleAssignments": { + "copy": { + "name": "MX_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, - "nullable": true + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/MX/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/MX', parameters('privateDnsZoneName'), 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": [ + "MX" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed MX record." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed MX record." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/MX', parameters('privateDnsZoneName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed MX record." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "privateDnsZone_PTR": { + "copy": { + "name": "privateDnsZone_PTR", + "count": "[length(coalesce(parameters('ptr'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-PTRRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('ptr'), createArray())[copyIndex()].name]" + }, + "metadata": { + "value": "[tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'metadata')]" + }, + "ptrRecords": { + "value": "[tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'ptrRecords')]" + }, + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'ttl'), 3600)]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'roleAssignments')]" + } + }, + "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.32.4.45862", + "templateHash": "3080404733048745471" }, - "ipConfigurationsType": { + "name": "Private DNS Zone PTR record", + "description": "This module deploys a Private DNS Zone PTR record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { "type": "array", "items": { "type": "object", "properties": { "name": { "type": "string", + "nullable": true, "metadata": { - "description": "Required. The name of the resource that is unique within a resource group." + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." } }, - "properties": { - "type": "object", - "properties": { - "groupId": { - "type": "string", - "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." - } - }, - "memberName": { - "type": "string", - "metadata": { - "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." - } - }, - "privateIPAddress": { - "type": "string", - "metadata": { - "description": "Required. A private IP address obtained from the private endpoint's subnet." - } - } - }, + "roleDefinitionIdOrName": { + "type": "string", "metadata": { - "description": "Required. Properties of private endpoint IP configurations." + "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'." } - } - } - }, - "nullable": true - }, - "manualPrivateLinkServiceConnectionsType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { + }, + "principalId": { "type": "string", "metadata": { - "description": "Required. The name of the private link service connection." + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." } }, - "properties": { - "type": "object", - "properties": { - "groupIds": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." - } - }, - "privateLinkServiceId": { - "type": "string", - "metadata": { - "description": "Required. The resource id of private link service." - } - }, - "requestMessage": { - "type": "string", - "metadata": { - "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." - } - } - }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, "metadata": { - "description": "Required. Properties of private link service connection." + "description": "Optional. The principal type of the assigned principal ID." } - } - } - }, - "nullable": true - }, - "privateLinkServiceConnectionsType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { + }, + "description": { "type": "string", + "nullable": true, "metadata": { - "description": "Required. The name of the private link service connection." + "description": "Optional. The description of the role assignment." } }, - "properties": { - "type": "object", - "properties": { - "groupIds": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." - } - }, - "privateLinkServiceId": { - "type": "string", - "metadata": { - "description": "Required. The resource id of private link service." - } - }, - "requestMessage": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." - } - } - }, + "condition": { + "type": "string", + "nullable": true, "metadata": { - "description": "Required. Properties of private link service connection." + "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\"." } - } - } - }, - "nullable": true - }, - "customDnsConfigType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "fqdn": { + }, + "conditionVersion": { "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, "metadata": { - "description": "Required. Fqdn that resolves to private endpoint IP address." + "description": "Optional. Version of the condition." } }, - "ipAddresses": { - "type": "array", - "items": { - "type": "string" - }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, "metadata": { - "description": "Required. A list of private IP addresses of the private endpoint." + "description": "Optional. The Resource Id of the delegated managed identity resource." } } } }, "nullable": true - }, - "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_imported_from!": { - "sourceTemplate": "private-dns-zone-group/main.bicep" - } - } } }, "parameters": { - "name": { + "privateDnsZoneName": { "type": "string", "metadata": { - "description": "Required. Name of the private endpoint resource to create." + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." } }, - "subnetResourceId": { + "name": { "type": "string", "metadata": { - "description": "Required. Resource ID of the subnet where the endpoint needs to be created." - } - }, - "applicationSecurityGroupResourceIds": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. Application security groups in which the private endpoint IP configuration is included." + "description": "Required. The name of the PTR record." } }, - "customNetworkInterfaceName": { - "type": "string", + "metadata": { + "type": "object", "nullable": true, "metadata": { - "description": "Optional. The custom name of the network interface attached to the private endpoint." - } - }, - "ipConfigurations": { - "$ref": "#/definitions/ipConfigurationsType", - "metadata": { - "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." + "description": "Optional. The metadata attached to the record set." } }, - "privateDnsZoneGroup": { - "$ref": "#/definitions/privateDnsZoneGroupType", + "ptrRecords": { + "type": "array", "nullable": true, "metadata": { - "description": "Optional. The private DNS zone group to configure for the private endpoint." - } - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]", - "metadata": { - "description": "Optional. Location for all Resources." + "description": "Optional. The list of PTR records in the record set." } }, - "lock": { - "$ref": "#/definitions/lockType", + "ttl": { + "type": "int", + "defaultValue": 3600, "metadata": { - "description": "Optional. The lock settings of the service." + "description": "Optional. The TTL (time-to-live) of the records in the record set." } }, "roleAssignments": { @@ -60599,38 +73297,6 @@ "metadata": { "description": "Optional. Array of role assignments to create." } - }, - "tags": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." - } - }, - "customDnsConfigs": { - "$ref": "#/definitions/customDnsConfigType", - "metadata": { - "description": "Optional. Custom DNS configurations." - } - }, - "manualPrivateLinkServiceConnections": { - "$ref": "#/definitions/manualPrivateLinkServiceConnectionsType", - "metadata": { - "description": "Optional. 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." - } - }, - "privateLinkServiceConnections": { - "$ref": "#/definitions/privateLinkServiceConnectionsType", - "metadata": { - "description": "Optional. A grouping of information about the connection to the remote resource." - } - }, - "enableTelemetry": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Optional. Enable/Disable usage telemetry for module." - } } }, "variables": { @@ -60643,87 +73309,40 @@ ], "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')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", "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 (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, "resources": { - "avmTelemetry": { - "condition": "[parameters('enableTelemetry')]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2024-03-01", - "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.7.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" - } - } - } - } + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" }, - "privateEndpoint": { - "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2023-11-01", - "name": "[parameters('name')]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", + "PTR": { + "type": "Microsoft.Network/privateDnsZones/PTR", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", "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')]" - } + "metadata": "[parameters('metadata')]", + "ptrRecords": "[parameters('ptrRecords')]", + "ttl": "[parameters('ttl')]" } }, - "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": "[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": { + "PTR_roleAssignments": { "copy": { - "name": "privateEndpoint_roleAssignments", + "name": "PTR_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))]", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/PTR/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/PTR', parameters('privateDnsZoneName'), 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]", @@ -60734,246 +73353,70 @@ "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" }, "dependsOn": [ - "privateEndpoint" - ] - }, - "privateEndpoint_privateDnsZoneGroup": { - "condition": "[not(empty(parameters('privateDnsZoneGroup')))]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-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.29.47.4906", - "templateHash": "5805178546717255803" - }, - "name": "Private Endpoint Private DNS Zone Groups", - "description": "This module deploys a Private Endpoint Private DNS Zone Group.", - "owner": "Azure/module-maintainers" - }, - "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 - } - } - }, - "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." - } - } - }, - "variables": { - "copy": [ - { - "name": "privateDnsZoneConfigsVar", - "count": "[length(parameters('privateDnsZoneConfigs'))]", - "input": { - "name": "[coalesce(tryGet(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')], 'name'), last(split(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId, '/')))]", - "properties": { - "privateDnsZoneId": "[parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId]" - } - } - } - ] - }, - "resources": { - "privateEndpoint": { - "existing": true, - "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2023-11-01", - "name": "[parameters('privateEndpointName')]" - }, - "privateDnsZoneGroup": { - "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2023-11-01", - "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", - "properties": { - "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" - }, - "dependsOn": [ - "privateEndpoint" - ] - } - }, - "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" + "PTR" ] } }, "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." + "description": "The name of the deployed PTR record." }, "value": "[parameters('name')]" }, - "location": { + "resourceId": { "type": "string", "metadata": { - "description": "The location the resource was deployed into." - }, - "value": "[reference('privateEndpoint', '2023-11-01', 'full').location]" - }, - "customDnsConfig": { - "$ref": "#/definitions/customDnsConfigType", - "metadata": { - "description": "The custom DNS configurations of the private endpoint." - }, - "value": "[reference('privateEndpoint').customDnsConfigs]" - }, - "networkInterfaceIds": { - "type": "array", - "metadata": { - "description": "The IDs of the network interfaces associated with the private endpoint." + "description": "The resource ID of the deployed PTR record." }, - "value": "[reference('privateEndpoint').networkInterfaces]" + "value": "[resourceId('Microsoft.Network/privateDnsZones/PTR', parameters('privateDnsZoneName'), parameters('name'))]" }, - "groupId": { + "resourceGroupName": { "type": "string", "metadata": { - "description": "The group Id for the private endpoint Group." + "description": "The resource group of the deployed PTR record." }, - "value": "[if(and(not(empty(reference('privateEndpoint').manualPrivateLinkServiceConnections)), greater(length(tryGet(reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties, 'groupIds')), 0)), coalesce(tryGet(reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties, 'groupIds', 0), ''), if(and(not(empty(reference('privateEndpoint').privateLinkServiceConnections)), greater(length(tryGet(reference('privateEndpoint').privateLinkServiceConnections[0].properties, 'groupIds')), 0)), coalesce(tryGet(reference('privateEndpoint').privateLinkServiceConnections[0].properties, 'groupIds', 0), ''), ''))]" + "value": "[resourceGroup().name]" } } } }, "dependsOn": [ - "searchService" + "privateDnsZone" ] }, - "searchService_sharedPrivateLinkResources": { + "privateDnsZone_SOA": { "copy": { - "name": "searchService_sharedPrivateLinkResources", - "count": "[length(parameters('sharedPrivateLinkResources'))]", - "mode": "serial", - "batchSize": 1 + "name": "privateDnsZone_SOA", + "count": "[length(coalesce(parameters('soa'), createArray()))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-searchService-SharedPrivateLink-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "name": "[format('{0}-PrivateDnsZone-SOARecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "name": { - "value": "[coalesce(tryGet(parameters('sharedPrivateLinkResources')[copyIndex()], 'name'), format('spl-{0}-{1}-{2}', last(split(resourceId('Microsoft.Search/searchServices', parameters('name')), '/')), parameters('sharedPrivateLinkResources')[copyIndex()].groupId, copyIndex()))]" - }, - "searchServiceName": { + "privateDnsZoneName": { "value": "[parameters('name')]" }, - "privateLinkResourceId": { - "value": "[parameters('sharedPrivateLinkResources')[copyIndex()].privateLinkResourceId]" + "name": { + "value": "[coalesce(parameters('soa'), createArray())[copyIndex()].name]" }, - "groupId": { - "value": "[parameters('sharedPrivateLinkResources')[copyIndex()].groupId]" + "metadata": { + "value": "[tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'metadata')]" }, - "requestMessage": { - "value": "[parameters('sharedPrivateLinkResources')[copyIndex()].requestMessage]" + "soaRecord": { + "value": "[tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'soaRecord')]" }, - "resourceRegion": { - "value": "[tryGet(parameters('sharedPrivateLinkResources')[copyIndex()], 'resourceRegion')]" + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'ttl'), 3600)]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'roleAssignments')]" } }, "template": { @@ -60983,89 +73426,206 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "16868169528574127897" + "version": "0.32.4.45862", + "templateHash": "6653951445614700931" }, - "name": "Search Services Private Link Resources", - "description": "This module deploys a Search Service Private Link Resource." + "name": "Private DNS Zone SOA record", + "description": "This module deploys a Private DNS Zone SOA record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "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." + } + } + } + }, + "nullable": true + } }, "parameters": { - "searchServiceName": { + "privateDnsZoneName": { "type": "string", "metadata": { - "description": "Conditional. The name of the parent searchServices. Required if the template is used in a standalone deployment." + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." } }, "name": { "type": "string", "metadata": { - "description": "Required. The name of the shared private link resource managed by the Azure Cognitive Search service within the specified resource group." + "description": "Required. The name of the SOA record." } }, - "privateLinkResourceId": { - "type": "string", + "metadata": { + "type": "object", + "nullable": true, "metadata": { - "description": "Required. The resource ID of the resource the shared private link resource is for." + "description": "Optional. The metadata attached to the record set." } }, - "groupId": { - "type": "string", + "soaRecord": { + "type": "object", + "nullable": true, "metadata": { - "description": "Required. The group ID from the provider of resource the shared private link resource is for." + "description": "Optional. A SOA record." } }, - "requestMessage": { - "type": "string", + "ttl": { + "type": "int", + "defaultValue": 3600, "metadata": { - "description": "Required. The request message for requesting approval of the shared private link resource." + "description": "Optional. The TTL (time-to-live) of the records in the record set." } }, - "resourceRegion": { - "type": "string", - "nullable": true, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", "metadata": { - "description": "Optional. Can be used to specify the Azure Resource Manager location of the resource to which a shared private link is to be created. This is only required for those resources whose DNS configuration are regional (such as Azure Kubernetes Service)." + "description": "Optional. Array of role assignments to create." + } + } + }, + "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')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, "resources": { - "searchService": { + "privateDnsZone": { "existing": true, - "type": "Microsoft.Search/searchServices", - "apiVersion": "2023-11-01", - "name": "[parameters('searchServiceName')]" + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" }, - "sharedPrivateLinkResource": { - "type": "Microsoft.Search/searchServices/sharedPrivateLinkResources", - "apiVersion": "2023-11-01", - "name": "[format('{0}/{1}', parameters('searchServiceName'), parameters('name'))]", + "SOA": { + "type": "Microsoft.Network/privateDnsZones/SOA", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", "properties": { - "privateLinkResourceId": "[parameters('privateLinkResourceId')]", - "groupId": "[parameters('groupId')]", - "requestMessage": "[parameters('requestMessage')]", - "resourceRegion": "[parameters('resourceRegion')]" + "metadata": "[parameters('metadata')]", + "soaRecord": "[parameters('soaRecord')]", + "ttl": "[parameters('ttl')]" } + }, + "SOA_roleAssignments": { + "copy": { + "name": "SOA_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/SOA/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/SOA', parameters('privateDnsZoneName'), 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": [ + "SOA" + ] } }, "outputs": { "name": { "type": "string", "metadata": { - "description": "The name of the shared private link resource." + "description": "The name of the deployed SOA record." }, "value": "[parameters('name')]" }, "resourceId": { "type": "string", "metadata": { - "description": "The resource ID of the shared private link resource." + "description": "The resource ID of the deployed SOA record." }, - "value": "[resourceId('Microsoft.Search/searchServices/sharedPrivateLinkResources', parameters('searchServiceName'), parameters('name'))]" + "value": "[resourceId('Microsoft.Network/privateDnsZones/SOA', parameters('privateDnsZoneName'), parameters('name'))]" }, "resourceGroupName": { "type": "string", "metadata": { - "description": "The name of the resource group the shared private link resource was created in." + "description": "The resource group of the deployed SOA record." }, "value": "[resourceGroup().name]" } @@ -61073,27 +73633,40 @@ } }, "dependsOn": [ - "searchService" + "privateDnsZone" ] }, - "secretsExport": { - "condition": "[not(equals(parameters('secretsExportConfiguration'), null()))]", + "privateDnsZone_SRV": { + "copy": { + "name": "privateDnsZone_SRV", + "count": "[length(coalesce(parameters('srv'), createArray()))]" + }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-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]]", + "name": "[format('{0}-PrivateDnsZone-SRVRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "keyVaultName": { - "value": "[last(split(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '/'))]" + "privateDnsZoneName": { + "value": "[parameters('name')]" }, - "secretsToSet": { - "value": "[union(createArray(), if(contains(parameters('secretsExportConfiguration'), 'primaryAdminKeyName'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'primaryAdminKeyName'), 'value', listAdminKeys(resourceId('Microsoft.Search/searchServices', parameters('name')), '2024-03-01-preview').primaryKey)), createArray()), if(contains(parameters('secretsExportConfiguration'), 'secondaryAdminKeyName'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'secondaryAdminKeyName'), 'value', listAdminKeys(resourceId('Microsoft.Search/searchServices', parameters('name')), '2024-03-01-preview').secondaryKey)), createArray()))]" + "name": { + "value": "[coalesce(parameters('srv'), createArray())[copyIndex()].name]" + }, + "metadata": { + "value": "[tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'metadata')]" + }, + "srvRecords": { + "value": "[tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'srvRecords')]" + }, + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'ttl'), 3600)]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'roleAssignments')]" } }, "template": { @@ -61103,1028 +73676,693 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "8130113748408624333" - } + "version": "0.32.4.45862", + "templateHash": "5790774778713328446" + }, + "name": "Private DNS Zone SRV record", + "description": "This module deploys a Private DNS Zone SRV record.", + "owner": "Azure/module-maintainers" }, "definitions": { - "secretSetType": { - "type": "object", - "properties": { - "secretResourceId": { - "type": "string", - "metadata": { - "description": "The resourceId of the exported secret." - } - }, - "secretUri": { - "type": "string", - "metadata": { - "description": "The secret URI of the exported secret." + "roleAssignmentType": { + "type": "array", + "items": { + "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": { - "__bicep_export!": true - } - }, - "secretToSetType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the secret to set." - } - }, - "value": { - "type": "securestring", - "metadata": { - "description": "Required. The value of the secret to set." - } - } - } + "nullable": true } }, "parameters": { - "keyVaultName": { + "privateDnsZoneName": { "type": "string", "metadata": { - "description": "Required. The name of the Key Vault to set the ecrets in." + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." } }, - "secretsToSet": { - "type": "array", - "items": { - "$ref": "#/definitions/secretToSetType" - }, + "name": { + "type": "string", "metadata": { - "description": "Required. The secrets to set in the Key Vault." + "description": "Required. The name of the SRV record." } - } - }, - "resources": { - "keyVault": { - "existing": true, - "type": "Microsoft.KeyVault/vaults", - "apiVersion": "2022-07-01", - "name": "[parameters('keyVaultName')]" }, - "secrets": { - "copy": { - "name": "secrets", - "count": "[length(parameters('secretsToSet'))]" - }, - "type": "Microsoft.KeyVault/vaults/secrets", - "apiVersion": "2023-07-01", - "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('secretsToSet')[copyIndex()].name)]", - "properties": { - "value": "[parameters('secretsToSet')[copyIndex()].value]" + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." } - } - }, - "outputs": { - "secretsSet": { + }, + "srvRecords": { "type": "array", - "items": { - "$ref": "#/definitions/secretSetType" - }, + "nullable": true, "metadata": { - "description": "The references to the secrets exported to the provided Key Vault." - }, - "copy": { - "count": "[length(range(0, length(coalesce(parameters('secretsToSet'), createArray()))))]", - "input": { - "secretResourceId": "[resourceId('Microsoft.KeyVault/vaults/secrets', parameters('keyVaultName'), parameters('secretsToSet')[range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()]].name)]", - "secretUri": "[reference(format('secrets[{0}]', range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()])).secretUri]" - } + "description": "Optional. The list of SRV records in the record set." } - } - } - } - }, - "dependsOn": [ - "searchService" - ] - } - }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the search service." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the search service." - }, - "value": "[resourceId('Microsoft.Search/searchServices', parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The name of the resource group the search service was created in." - }, - "value": "[resourceGroup().name]" - }, - "systemAssignedMIPrincipalId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "The principal ID of the system assigned identity." - }, - "value": "[tryGet(tryGet(reference('searchService', '2024-03-01-preview', 'full'), 'identity'), 'principalId')]" - }, - "location": { - "type": "string", - "metadata": { - "description": "The location the resource was deployed into." - }, - "value": "[reference('searchService', '2024-03-01-preview', 'full').location]" - }, - "exportedSecrets": { - "$ref": "#/definitions/secretsOutputType", - "metadata": { - "description": "A hashtable of references to the secrets exported to the provided Key Vault. The key of each reference is each secret's name." - }, - "value": "[if(not(equals(parameters('secretsExportConfiguration'), null())), toObject(reference('secretsExport').outputs.secretsSet.value, lambda('secret', last(split(lambdaVariables('secret').secretResourceId, '/'))), lambda('secret', lambdaVariables('secret'))), createObject())]" - } - } - } - }, - "dependsOn": [ - "privateDnsZone" - ] - } - }, - "outputs": { - "resourceId": { - "type": "string", - "value": "[reference('aiSearch').outputs.resourceId.value]" - }, - "name": { - "type": "string", - "value": "[reference('aiSearch').outputs.name.value]" - }, - "systemAssignedMIPrincipalId": { - "type": "string", - "value": "[coalesce(tryGet(tryGet(reference('aiSearch').outputs, 'systemAssignedMIPrincipalId'), 'value'), '')]" - } - } - } - }, - "dependsOn": [ - "cognitiveServices", - "logAnalyticsWorkspace", - "network" - ] - }, - "virtualMachine": { - "condition": "[parameters('networkIsolation')]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[take(format('{0}-virtual-machine-deployment', parameters('name')), 64)]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "vmName": { - "value": "[toLower(format('vm-{0}-jump', parameters('name')))]" - }, - "vmNicName": { - "value": "[toLower(format('nic-vm-{0}-jump', parameters('name')))]" - }, - "vmSize": { - "value": "[parameters('vmSize')]" - }, - "vmSubnetId": { - "value": "[reference('network').outputs.vmSubnetId.value]" - }, - "storageAccountName": { - "value": "[reference('storageAccount').outputs.storageName.value]" - }, - "storageAccountResourceGroup": { - "value": "[resourceGroup().name]" - }, - "imagePublisher": { - "value": "MicrosoftWindowsDesktop" - }, - "imageOffer": { - "value": "Windows-11" - }, - "imageSku": { - "value": "win11-23h2-ent" - }, - "authenticationType": { - "value": "password" - }, - "vmAdminUsername": { - "value": "[variables('servicesUsername')]" - }, - "vmAdminPasswordOrKey": { - "value": "[parameters('vmAdminPasswordOrKey')]" - }, - "diskStorageAccountType": { - "value": "Premium_LRS" - }, - "numDataDisks": { - "value": 1 - }, - "osDiskSize": { - "value": 128 - }, - "dataDiskSize": { - "value": 50 - }, - "dataDiskCaching": { - "value": "ReadWrite" - }, - "enableAcceleratedNetworking": { - "value": true - }, - "enableMicrosoftEntraIdAuth": { - "value": true - }, - "userObjectId": { - "value": "[parameters('userObjectId')]" - }, - "workspaceId": { - "value": "[reference('logAnalyticsWorkspace').outputs.resourceId.value]" - }, - "location": { - "value": "[parameters('location')]" - }, - "tags": { - "value": "[variables('allTags')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "14156198524686936179" - } - }, - "parameters": { - "vmName": { - "type": "string", - "defaultValue": "TestVm", - "metadata": { - "description": "Specifies the name of the virtual machine." - } - }, - "vmSize": { - "type": "string", - "defaultValue": "Standard_DS4_v2", - "metadata": { - "description": "Specifies the size of the virtual machine." - } - }, - "vmSubnetId": { - "type": "string", - "metadata": { - "description": "Specifies the resource id of the subnet hosting the virtual machine." - } - }, - "storageAccountName": { - "type": "string", - "metadata": { - "description": "Specifies the name of the storage account where the bootstrap diagnostic logs of the virtual machine are stored." - } - }, - "storageAccountResourceGroup": { - "type": "string", - "metadata": { - "description": "Specifies the resource group of the storage account where the bootstrap diagnostic logs of the virtual machine are stored." - } - }, - "imagePublisher": { - "type": "string", - "defaultValue": "MicrosoftWindowsServer", - "metadata": { - "description": "Specifies the image publisher of the disk image used to create the virtual machine." - } - }, - "imageOffer": { - "type": "string", - "defaultValue": "WindowsServer", - "metadata": { - "description": "Specifies the offer of the platform image or marketplace image used to create the virtual machine." - } - }, - "imageSku": { - "type": "string", - "defaultValue": "2022-datacenter-azure-edition", - "metadata": { - "description": "Specifies the image version for the virtual machine." - } - }, - "authenticationType": { - "type": "string", - "defaultValue": "password", - "allowedValues": [ - "sshPublicKey", - "password" - ], - "metadata": { - "description": "Specifies the type of authentication when accessing the Virtual Machine. SSH key is recommended." - } - }, - "vmAdminUsername": { - "type": "string", - "metadata": { - "description": "Specifies the name of the administrator account of the virtual machine." - } - }, - "vmAdminPasswordOrKey": { - "type": "securestring", - "metadata": { - "description": "Specifies the SSH Key or password for the virtual machine. SSH key is recommended." - } - }, - "diskStorageAccountType": { - "type": "string", - "defaultValue": "Premium_LRS", - "allowedValues": [ - "Premium_LRS", - "StandardSSD_LRS", - "Standard_LRS", - "UltraSSD_LRS" - ], - "metadata": { - "description": "Specifies the storage account type for OS and data disk." - } - }, - "numDataDisks": { - "type": "int", - "defaultValue": 1, - "minValue": 0, - "maxValue": 64, - "metadata": { - "description": "Specifies the number of data disks of the virtual machine." - } - }, - "osDiskSize": { - "type": "int", - "defaultValue": 128, - "metadata": { - "description": "Specifies the size in GB of the OS disk of the VM." - } - }, - "dataDiskSize": { - "type": "int", - "defaultValue": 50, - "metadata": { - "description": "Specifies the size in GB of the OS disk of the virtual machine." - } - }, - "dataDiskCaching": { - "type": "string", - "defaultValue": "ReadWrite", - "metadata": { - "description": "Specifies the caching requirements for the data disks." - } - }, - "enableMicrosoftEntraIdAuth": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Specifies whether enabling Microsoft Entra ID authentication on the virtual machine." - } - }, - "enableAcceleratedNetworking": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Specifies whether enabling accelerated networking on the virtual machine." - } - }, - "vmNicName": { - "type": "string", - "metadata": { - "description": "Specifies the name of the network interface of the virtual machine." - } - }, - "userObjectId": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Specifies the object id of a Microsoft Entra ID user. In general, this the object id of the system administrator who deploys the Azure resources." - } - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]", - "metadata": { - "description": "Specifies the location." - } - }, - "workspaceId": { - "type": "string", - "metadata": { - "description": "Specifies the resource id of the Log Analytics workspace." - } - }, - "tags": { - "type": "object", - "metadata": { - "description": "Specifies the resource tags." - } - } - }, - "variables": { - "randomString": "[uniqueString(resourceGroup().id, parameters('vmName'), parameters('vmAdminPasswordOrKey'))]", - "adminPassword": "[if(less(length(parameters('vmAdminPasswordOrKey')), 8), format('{0}{1}', parameters('vmAdminPasswordOrKey'), take(variables('randomString'), 12)), parameters('vmAdminPasswordOrKey'))]", - "linuxConfiguration": { - "disablePasswordAuthentication": true, - "ssh": { - "publicKeys": [ - { - "path": "[format('/home/{0}/.ssh/authorized_keys', parameters('vmAdminUsername'))]", - "keyData": "[variables('adminPassword')]" - } - ] - }, - "provisionVMAgent": true - } - }, - "resources": [ - { - "type": "Microsoft.Network/networkInterfaces", - "apiVersion": "2021-08-01", - "name": "[parameters('vmNicName')]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", - "properties": { - "enableAcceleratedNetworking": "[parameters('enableAcceleratedNetworking')]", - "ipConfigurations": [ - { - "name": "ipconfig1", - "properties": { - "privateIPAllocationMethod": "Dynamic", - "subnet": { - "id": "[parameters('vmSubnetId')]" - } - } - } - ] - } - }, - { - "type": "Microsoft.Compute/virtualMachines", - "apiVersion": "2021-11-01", - "name": "[parameters('vmName')]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", - "properties": { - "hardwareProfile": { - "vmSize": "[parameters('vmSize')]" - }, - "osProfile": { - "computerName": "[take(parameters('vmName'), 15)]", - "adminUsername": "[parameters('vmAdminUsername')]", - "adminPassword": "[variables('adminPassword')]", - "linuxConfiguration": "[if(equals(parameters('authenticationType'), 'password'), null(), variables('linuxConfiguration'))]" - }, - "storageProfile": { - "copy": [ - { - "name": "dataDisks", - "count": "[length(range(0, parameters('numDataDisks')))]", - "input": { - "caching": "[parameters('dataDiskCaching')]", - "diskSizeGB": "[parameters('dataDiskSize')]", - "lun": "[range(0, parameters('numDataDisks'))[copyIndex('dataDisks')]]", - "name": "[format('{0}-DataDisk{1}', parameters('vmName'), range(0, parameters('numDataDisks'))[copyIndex('dataDisks')])]", - "createOption": "Empty", - "managedDisk": { - "storageAccountType": "[parameters('diskStorageAccountType')]" + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "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')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "SRV": { + "type": "Microsoft.Network/privateDnsZones/SRV", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "properties": { + "metadata": "[parameters('metadata')]", + "srvRecords": "[parameters('srvRecords')]", + "ttl": "[parameters('ttl')]" + } + }, + "SRV_roleAssignments": { + "copy": { + "name": "SRV_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/SRV/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/SRV', parameters('privateDnsZoneName'), 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": [ + "SRV" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed SRV record." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed SRV record." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/SRV', parameters('privateDnsZoneName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed SRV record." + }, + "value": "[resourceGroup().name]" + } + } } - } - } - ], - "imageReference": { - "publisher": "[parameters('imagePublisher')]", - "offer": "[parameters('imageOffer')]", - "sku": "[parameters('imageSku')]", - "version": "latest" - }, - "osDisk": { - "name": "[format('{0}_OSDisk', parameters('vmName'))]", - "caching": "ReadWrite", - "createOption": "FromImage", - "diskSizeGB": "[parameters('osDiskSize')]", - "managedDisk": { - "storageAccountType": "[parameters('diskStorageAccountType')]" - } - } - }, - "networkProfile": { - "networkInterfaces": [ - { - "id": "[resourceId('Microsoft.Network/networkInterfaces', parameters('vmNicName'))]" - } - ] - }, - "diagnosticsProfile": { - "bootDiagnostics": { - "enabled": true, - "storageUri": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('storageAccountResourceGroup')), 'Microsoft.Storage/storageAccounts', parameters('storageAccountName')), '2021-09-01').primaryEndpoints.blob]" - } - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Network/networkInterfaces', parameters('vmNicName'))]" - ] - }, - { - "type": "Microsoft.Compute/virtualMachines/extensions", - "apiVersion": "2023-09-01", - "name": "[format('{0}/{1}', parameters('vmName'), 'DependencyAgentWindows')]", - "location": "[parameters('location')]", - "properties": { - "publisher": "Microsoft.Azure.Monitoring.DependencyAgent", - "type": "DependencyAgentWindows", - "typeHandlerVersion": "9.4", - "autoUpgradeMinorVersion": true, - "enableAutomaticUpgrade": true - }, - "dependsOn": [ - "[resourceId('Microsoft.Compute/virtualMachines', parameters('vmName'))]" - ] - }, - { - "type": "Microsoft.Compute/virtualMachines/extensions", - "apiVersion": "2023-09-01", - "name": "[format('{0}/{1}', parameters('vmName'), 'AzureMonitorWindowsAgent')]", - "location": "[parameters('location')]", - "properties": { - "publisher": "Microsoft.Azure.Monitor", - "type": "AzureMonitorWindowsAgent", - "typeHandlerVersion": "1.0", - "autoUpgradeMinorVersion": true, - "enableAutomaticUpgrade": true - }, - "dependsOn": [ - "[resourceId('Microsoft.Compute/virtualMachines/extensions', parameters('vmName'), 'DependencyAgentWindows')]", - "[resourceId('Microsoft.Compute/virtualMachines', parameters('vmName'))]" - ] - }, - { - "condition": "[parameters('enableMicrosoftEntraIdAuth')]", - "type": "Microsoft.Compute/virtualMachines/extensions", - "apiVersion": "2023-09-01", - "name": "[format('{0}/{1}', parameters('vmName'), 'AADLoginForWindows')]", - "location": "[parameters('location')]", - "properties": { - "publisher": "Microsoft.Azure.ActiveDirectory", - "type": "AADLoginForWindows", - "typeHandlerVersion": "1.0", - "autoUpgradeMinorVersion": false, - "enableAutomaticUpgrade": false - }, - "dependsOn": [ - "[resourceId('Microsoft.Compute/virtualMachines/extensions', parameters('vmName'), 'AzureMonitorWindowsAgent')]", - "[resourceId('Microsoft.Compute/virtualMachines', parameters('vmName'))]" - ] - }, - { - "type": "Microsoft.Insights/dataCollectionRules", - "apiVersion": "2022-06-01", - "name": "DCR-Win-Event-Logs-to-LAW", - "location": "[parameters('location')]", - "kind": "Windows", - "properties": { - "dataFlows": [ - { - "destinations": [ - "logAnalytics" - ], - "streams": [ - "Microsoft-Event" - ] - } - ], - "dataSources": { - "windowsEventLogs": [ - { - "streams": [ - "Microsoft-Event" - ], - "xPathQueries": [ - "Application!*[System[(Level=1 or Level=2 or Level=3 or or Level=0) ]]", - "Security!*[System[(band(Keywords,13510798882111488))]]", - "System!*[System[(Level=1 or Level=2 or Level=3 or or Level=0)]]" - ], - "name": "eventLogsDataSource" - } - ] - }, - "description": "Collect Windows Event Logs and send to Azure Monitor Logs", - "destinations": { - "logAnalytics": [ - { - "name": "logAnalytics", - "workspaceResourceId": "[parameters('workspaceId')]" - } - ] - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Compute/virtualMachines/extensions', parameters('vmName'), 'AADLoginForWindows')]" - ] - }, - { - "type": "Microsoft.Insights/dataCollectionRules", - "apiVersion": "2022-06-01", - "name": "DCR-Win-Perf-to-LAW", - "location": "[parameters('location')]", - "kind": "Windows", - "properties": { - "dataFlows": [ - { - "destinations": [ - "logAnalytics" - ], - "streams": [ - "Microsoft-Perf" - ] - } - ], - "dataSources": { - "performanceCounters": [ - { - "counterSpecifiers": [ - "\\Processor Information(_Total)\\% Processor Time", - "\\Processor Information(_Total)\\% Privileged Time", - "\\Processor Information(_Total)\\% User Time", - "\\Processor Information(_Total)\\Processor Frequency", - "\\System\\Processes", - "\\Process(_Total)\\Thread Count", - "\\Process(_Total)\\Handle Count", - "\\System\\System Up Time", - "\\System\\Context Switches/sec", - "\\System\\Processor Queue Length", - "\\Memory\\% Committed Bytes In Use", - "\\Memory\\Available Bytes", - "\\Memory\\Committed Bytes", - "\\Memory\\Cache Bytes", - "\\Memory\\Pool Paged Bytes", - "\\Memory\\Pool Nonpaged Bytes", - "\\Memory\\Pages/sec", - "\\Memory\\Page Faults/sec", - "\\Process(_Total)\\Working Set", - "\\Process(_Total)\\Working Set - Private", - "\\LogicalDisk(_Total)\\% Disk Time", - "\\LogicalDisk(_Total)\\% Disk Read Time", - "\\LogicalDisk(_Total)\\% Disk Write Time", - "\\LogicalDisk(_Total)\\% Idle Time", - "\\LogicalDisk(_Total)\\Disk Bytes/sec", - "\\LogicalDisk(_Total)\\Disk Read Bytes/sec", - "\\LogicalDisk(_Total)\\Disk Write Bytes/sec", - "\\LogicalDisk(_Total)\\Disk Transfers/sec", - "\\LogicalDisk(_Total)\\Disk Reads/sec", - "\\LogicalDisk(_Total)\\Disk Writes/sec", - "\\LogicalDisk(_Total)\\Avg. Disk sec/Transfer", - "\\LogicalDisk(_Total)\\Avg. Disk sec/Read", - "\\LogicalDisk(_Total)\\Avg. Disk sec/Write", - "\\LogicalDisk(_Total)\\Avg. Disk Queue Length", - "\\LogicalDisk(_Total)\\Avg. Disk Read Queue Length", - "\\LogicalDisk(_Total)\\Avg. Disk Write Queue Length", - "\\LogicalDisk(_Total)\\% Free Space", - "\\LogicalDisk(_Total)\\Free Megabytes", - "\\Network Interface(*)\\Bytes Total/sec", - "\\Network Interface(*)\\Bytes Sent/sec", - "\\Network Interface(*)\\Bytes Received/sec", - "\\Network Interface(*)\\Packets/sec", - "\\Network Interface(*)\\Packets Sent/sec", - "\\Network Interface(*)\\Packets Received/sec", - "\\Network Interface(*)\\Packets Outbound Errors", - "\\Network Interface(*)\\Packets Received Errors" - ], - "name": "perfCounterDataSource60", - "samplingFrequencyInSeconds": 60, - "streams": [ - "Microsoft-Perf" + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "privateDnsZone_TXT": { + "copy": { + "name": "privateDnsZone_TXT", + "count": "[length(coalesce(parameters('txt'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-TXTRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('txt'), createArray())[copyIndex()].name]" + }, + "metadata": { + "value": "[tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'metadata')]" + }, + "txtRecords": { + "value": "[tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'txtRecords')]" + }, + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'ttl'), 3600)]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'roleAssignments')]" + } + }, + "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.32.4.45862", + "templateHash": "1855369119498044639" + }, + "name": "Private DNS Zone TXT record", + "description": "This module deploys a Private DNS Zone TXT record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "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." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the TXT record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "txtRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of TXT records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "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')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "TXT": { + "type": "Microsoft.Network/privateDnsZones/TXT", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "properties": { + "metadata": "[parameters('metadata')]", + "ttl": "[parameters('ttl')]", + "txtRecords": "[parameters('txtRecords')]" + } + }, + "TXT_roleAssignments": { + "copy": { + "name": "TXT_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/TXT/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/TXT', parameters('privateDnsZoneName'), 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": [ + "TXT" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed TXT record." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed TXT record." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/TXT', parameters('privateDnsZoneName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed TXT record." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "privateDnsZone_virtualNetworkLinks": { + "copy": { + "name": "privateDnsZone_virtualNetworkLinks", + "count": "[length(coalesce(parameters('virtualNetworkLinks'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-VirtualNetworkLink-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'name'), format('{0}-vnetlink', last(split(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()].virtualNetworkResourceId, '/'))))]" + }, + "virtualNetworkResourceId": { + "value": "[coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()].virtualNetworkResourceId]" + }, + "location": { + "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'location'), 'global')]" + }, + "registrationEnabled": { + "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'registrationEnabled'), false())]" + }, + "tags": { + "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + }, + "resolutionPolicy": { + "value": "[tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'resolutionPolicy')]" + } + }, + "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.32.4.45862", + "templateHash": "15326596012552051215" + }, + "name": "Private DNS Zone Virtual Network Link", + "description": "This module deploys a Private DNS Zone Virtual Network Link.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "defaultValue": "[format('{0}-vnetlink', last(split(parameters('virtualNetworkResourceId'), '/')))]", + "metadata": { + "description": "Optional. The name of the virtual network link." + } + }, + "location": { + "type": "string", + "defaultValue": "global", + "metadata": { + "description": "Optional. The location of the PrivateDNSZone. Should be global." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "registrationEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Is auto-registration of virtual machine records in the virtual network in the Private DNS zone enabled?." + } + }, + "virtualNetworkResourceId": { + "type": "string", + "metadata": { + "description": "Required. Link to another virtual network resource ID." + } + }, + "resolutionPolicy": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resolution policy on the virtual network link. Only applicable for virtual network links to privatelink zones, and for A,AAAA,CNAME queries. When set to `NxDomainRedirect`, Azure DNS resolver falls back to public resolution if private dns query resolution results in non-existent domain response. `Default` is configured as the default option." + } + } + }, + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "virtualNetworkLink": { + "type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks", + "apiVersion": "2024-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "registrationEnabled": "[parameters('registrationEnabled')]", + "virtualNetwork": { + "id": "[parameters('virtualNetworkResourceId')]" + }, + "resolutionPolicy": "[parameters('resolutionPolicy')]" + } + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed virtual network link." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed virtual network link." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/virtualNetworkLinks', parameters('privateDnsZoneName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed virtual network link." + }, + "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('virtualNetworkLink', '2024-06-01', 'full').location]" + } + } + } + }, + "dependsOn": [ + "privateDnsZone" ] } - ] - }, - "description": "Collect Performance Counters and send to Azure Monitor Logs.", - "destinations": { - "logAnalytics": [ - { - "name": "logAnalytics", - "workspaceResourceId": "[parameters('workspaceId')]" + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the private DNS zone was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the private DNS zone." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the private DNS zone." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones', parameters('name'))]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('privateDnsZone', '2020-06-01', 'full').location]" } - ] + } } - }, - "dependsOn": [ - "[resourceId('Microsoft.Compute/virtualMachines/extensions', parameters('vmName'), 'AADLoginForWindows')]" - ] - }, - { - "type": "Microsoft.Insights/dataCollectionRuleAssociations", - "apiVersion": "2022-06-01", - "scope": "[format('Microsoft.Compute/virtualMachines/{0}', parameters('vmName'))]", - "name": "DCRA-VMSS-WEL-LAW", - "properties": { - "description": "Association of data collection rule. Deleting this association will break the data collection for this virtual machine.", - "dataCollectionRuleId": "[resourceId('Microsoft.Insights/dataCollectionRules', 'DCR-Win-Event-Logs-to-LAW')]" - }, - "dependsOn": [ - "[resourceId('Microsoft.Insights/dataCollectionRules', 'DCR-Win-Event-Logs-to-LAW')]", - "[resourceId('Microsoft.Compute/virtualMachines', parameters('vmName'))]" - ] - }, - { - "type": "Microsoft.Insights/dataCollectionRuleAssociations", - "apiVersion": "2022-06-01", - "scope": "[format('Microsoft.Compute/virtualMachines/{0}', parameters('vmName'))]", - "name": "DCRA-VM-PC-LAW", - "properties": { - "description": "Association of data collection rule. Deleting this association will break the data collection for this virtual machine.", - "dataCollectionRuleId": "[resourceId('Microsoft.Insights/dataCollectionRules', 'DCR-Win-Perf-to-LAW')]" - }, - "dependsOn": [ - "[resourceId('Microsoft.Insights/dataCollectionRules', 'DCR-Win-Perf-to-LAW')]", - "[resourceId('Microsoft.Compute/virtualMachines', parameters('vmName'))]" - ] - }, - { - "condition": "[and(parameters('enableMicrosoftEntraIdAuth'), not(empty(parameters('userObjectId'))))]", - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Compute/virtualMachines/{0}', parameters('vmName'))]", - "name": "[guid(resourceId('Microsoft.Compute/virtualMachines', parameters('vmName')), subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1c0163c0-47e6-4577-8991-ea5c82e286e4'), parameters('userObjectId'))]", - "properties": { - "roleDefinitionId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1c0163c0-47e6-4577-8991-ea5c82e286e4')]", - "principalType": "User", - "principalId": "[parameters('userObjectId')]" - }, - "dependsOn": [ - "[resourceId('Microsoft.Compute/virtualMachines', parameters('vmName'))]" - ] - } - ], - "outputs": { - "name": { - "type": "string", - "value": "[parameters('vmName')]" - }, - "id": { - "type": "string", - "value": "[resourceId('Microsoft.Compute/virtualMachines', parameters('vmName'))]" - } - } - } - }, - "dependsOn": [ - "logAnalyticsWorkspace", - "network", - "storageAccount" - ] - }, - "apim": { - "condition": "[parameters('apiManagementEnabled')]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[take(format('{0}-apim-deployment', parameters('name')), 64)]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[toLower(format('apim-{0}{1}', parameters('name'), variables('resourceToken')))]" - }, - "location": { - "value": "[parameters('location')]" - }, - "publisherEmail": { - "value": "[parameters('apiManagementPublisherEmail')]" - }, - "publisherName": { - "value": "[format('{0} API Management', parameters('name'))]" - }, - "sku": { - "value": "Developer" - }, - "networkIsolation": { - "value": "[parameters('networkIsolation')]" - }, - "logAnalyticsWorkspaceResourceId": { - "value": "[reference('logAnalyticsWorkspace').outputs.resourceId.value]" - }, - "virtualNetworkResourceId": "[if(parameters('networkIsolation'), createObject('value', reference('network').outputs.virtualNetworkId.value), createObject('value', ''))]", - "virtualNetworkSubnetResourceId": "[if(parameters('networkIsolation'), createObject('value', reference('network').outputs.vmSubnetId.value), createObject('value', ''))]", - "tags": { - "value": "[variables('allTags')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "9207373860269569676" - } - }, - "parameters": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the API Management service." - } - }, - "location": { - "type": "string", - "metadata": { - "description": "The location of the API Management service." - } - }, - "publisherName": { - "type": "string", - "metadata": { - "description": "Name of the API Management publisher." - } - }, - "publisherEmail": { - "type": "string", - "metadata": { - "description": "The email address of the API Management publisher." - } - }, - "sku": { - "type": "string", - "allowedValues": [ - "Consumption", - "Developer", - "Basic", - "Standard", - "Premium", - "StandardV2", - "BasicV2" - ], - "metadata": { - "description": "Optional. The pricing tier of this API Management service." - } - }, - "networkIsolation": { - "type": "bool", - "metadata": { - "description": "Specifies whether to create a private endpoint for the API Management service." } }, - "logAnalyticsWorkspaceResourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the Log Analytics workspace to use for diagnostic settings." - } - }, - "virtualNetworkResourceId": { - "type": "string", - "metadata": { - "description": "Resource ID of the virtual network to link the private DNS zones." - } - }, - "virtualNetworkSubnetResourceId": { - "type": "string", - "metadata": { - "description": "Resource ID of the subnet for the private endpoint." - } - }, - "tags": { - "type": "object", - "defaultValue": {}, - "metadata": { - "description": "Optional tags to be applied to the resources." - } - } - }, - "resources": [ { + "condition": "[parameters('networkIsolation')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[take(format('{0}-apim-deployment', parameters('name')), 64)]", + "name": "[take(format('{0}-apim-private-endpoint-deployment', parameters('name')), 64)]", "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[parameters('name')]" - }, - "location": { - "value": "[parameters('location')]" - }, - "tags": { - "value": "[parameters('tags')]" - }, - "sku": { - "value": "[parameters('sku')]" - }, - "publisherEmail": { - "value": "[parameters('publisherEmail')]" - }, - "publisherName": { - "value": "[parameters('publisherName')]" - }, - "virtualNetworkType": "[if(parameters('networkIsolation'), createObject('value', 'Internal'), createObject('value', 'None'))]", - "managedIdentities": { - "value": { - "systemAssigned": true - } - }, - "apis": { - "value": [ - { - "apiVersionSet": { - "name": "echo-version-set", - "properties": { - "description": "An echo API version set", - "displayName": "Echo version set", - "versioningScheme": "Segment" - } - }, - "description": "An echo API service", - "displayName": "Echo API", - "name": "echo-api", - "path": "echo", - "protocols": [ - "https" - ], - "serviceUrl": "https://echoapi.cloudapp.net/api" - } - ] - }, - "customProperties": { - "value": { - "Microsoft.WindowsAzure.ApiManagement.Gateway.Protocols.Server.Http2": "True", - "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Ssl30": "False", - "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Tls10": "False", - "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Tls11": "False", - "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA": "False", - "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA": "False", - "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_CBC_SHA": "False", - "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_CBC_SHA256": "False", - "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_GCM_SHA256": "False", - "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_256_CBC_SHA": "False", - "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_256_CBC_SHA256": "False", - "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TripleDes168": "False", - "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Ssl30": "False", - "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Tls10": "False", - "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Tls11": "False" - } - }, - "diagnosticSettings": { - "value": [ - { - "workspaceResourceId": "[parameters('logAnalyticsWorkspaceResourceId')]" - } - ] - }, - "products": { - "value": [ - { - "apis": [ - { - "name": "echo-api" - } - ], - "approvalRequired": true, - "description": "This is an echo API", - "displayName": "Echo API", - "groups": [ - { - "name": "developers" - } - ], - "name": "Starter", - "subscriptionRequired": true, - "terms": "By accessing or using the services provided by Echo API through Azure API Management, you agree to be bound by these Terms of Use. These terms may be updated from time to time, and your continued use of the services constitutes acceptance of any changes." - } - ] + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[toLower(format('pep-{0}', reference(resourceId('Microsoft.Resources/deployments', take(format('{0}-apim-deployment', parameters('name')), 64)), '2022-09-01').outputs.name.value))]" }, - "subscriptions": { + "subnetResourceId": { + "value": "[parameters('virtualNetworkSubnetResourceId')]" + }, + "privateDnsZoneGroup": { + "value": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "[reference(resourceId('Microsoft.Resources/deployments', 'private-dns-apim-deployment'), '2022-09-01').outputs.resourceId.value]" + } + ] + } + }, + "privateLinkServiceConnections": { "value": [ { - "displayName": "testArmSubscriptionAllApis", - "name": "testArmSubscriptionAllApis", - "scope": "/apis" + "name": "[reference(resourceId('Microsoft.Resources/deployments', take(format('{0}-apim-deployment', parameters('name')), 64)), '2022-09-01').outputs.name.value]", + "properties": { + "groupIds": [ + "Gateway" + ], + "privateLinkServiceId": "[reference(resourceId('Microsoft.Resources/deployments', take(format('{0}-apim-deployment', parameters('name')), 64)), '2022-09-01').outputs.resourceId.value]" + } } ] } @@ -62136,274 +74374,143 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.34.1.11899", - "templateHash": "7370685983298413612" + "version": "0.34.44.8038", + "templateHash": "12389807800450456797" }, - "name": "API Management Services", - "description": "This module deploys an API Management Service. The default deployment is set to use a Premium SKU to align with Microsoft WAF-aligned best practices. In most cases, non-prod deployments should use a lower-tier SKU." + "name": "Private Endpoints", + "description": "This module deploys a Private Endpoint." }, "definitions": { - "authorizationServerType": { + "privateDnsZoneGroupType": { "type": "object", "properties": { "name": { "type": "string", - "metadata": { - "description": "Required. Identifier of the authorization server." - } - }, - "displayName": { - "type": "string", - "maxLength": 50, - "metadata": { - "description": "Required. API Management Service Authorization Servers name. Must be 1 to 50 characters long." - } - }, - "authorizationEndpoint": { - "type": "string", - "metadata": { - "description": "Required. OAuth authorization endpoint. See ." - } - }, - "authorizationMethods": { - "type": "array", - "items": { - "type": "string" - }, - "nullable": true, - "metadata": { - "description": "Optional. HTTP verbs supported by the authorization endpoint. GET must be always present. POST is optional. - HEAD, OPTIONS, TRACE, GET, POST, PUT, PATCH, DELETE." - } - }, - "bearerTokenSendingMethods": { - "type": "array", - "items": { - "type": "string" - }, "nullable": true, "metadata": { - "description": "Optional. Specifies the mechanism by which access token is passed to the API. - authorizationHeader or query." + "description": "Optional. The name of the Private DNS Zone Group." } }, - "clientAuthenticationMethod": { + "privateDnsZoneGroupConfigs": { "type": "array", "items": { - "type": "string" + "$ref": "#/definitions/privateDnsZoneGroupConfigType" }, - "nullable": true, - "metadata": { - "description": "Optional. Method of authentication supported by the token endpoint of this authorization server. Possible values are Basic and/or Body. When Body is specified, client credentials and other parameters are passed within the request body in the application/x-www-form-urlencoded format. - Basic or Body." - } - }, - "clientId": { - "type": "securestring", - "metadata": { - "description": "Required. Client or app ID registered with this authorization server." - } - }, - "clientRegistrationEndpoint": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Optional reference to a page where client or app registration for this authorization server is performed. Contains absolute URL to entity being referenced." - } - }, - "clientSecret": { - "type": "securestring", - "metadata": { - "description": "Required. Client or app secret registered with this authorization server. This property will not be filled on 'GET' operations! Use '/listSecrets' POST request to get the value." - } - }, - "defaultScope": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Access token scope that is going to be requested by default. Can be overridden at the API level. Should be provided in the form of a string containing space-delimited values." - } - }, - "serverDescription": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Description of the authorization server. Can contain HTML formatting tags." - } - }, - "grantTypes": { - "type": "array", - "allowedValues": [ - "authorizationCode", - "clientCredentials", - "implicit", - "resourceOwnerPassword" - ], - "metadata": { - "description": "Required. Form of an authorization grant, which the client uses to request the access token. - authorizationCode, implicit, resourceOwnerPassword, clientCredentials." - } - }, - "resourceOwnerPassword": { - "type": "securestring", - "nullable": true, "metadata": { - "description": "Optional. Can be optionally specified when resource owner password grant type is supported by this authorization server. Default resource owner password." + "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." } - }, - "resourceOwnerUsername": { + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "ipConfigurationType": { + "type": "object", + "properties": { + "name": { "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Can be optionally specified when resource owner password grant type is supported by this authorization server. Default resource owner username." - } - }, - "supportState": { - "type": "bool", - "nullable": true, "metadata": { - "description": "Optional. If true, authorization server will include state parameter from the authorization request to its response. Client may use state parameter to raise protocol security." + "description": "Required. The name of the resource that is unique within a resource group." } }, - "tokenBodyParameters": { - "type": "array", - "items": { - "$ref": "#/definitions/tokenBodyParameterType" + "properties": { + "type": "object", + "properties": { + "groupId": { + "type": "string", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } + } }, - "nullable": true, - "metadata": { - "description": "Optional. Additional parameters required by the token endpoint of this authorization server represented as an array of JSON objects with name and value string properties, i.e. {\"name\" : \"name value\", \"value\": \"a value\"}. - TokenBodyParameterContract object." - } - }, - "tokenEndpoint": { - "type": "string", - "nullable": true, "metadata": { - "description": "Optional. OAuth token endpoint. Contains absolute URI to entity being referenced." + "description": "Required. Properties of private endpoint IP configurations." } } }, "metadata": { - "__bicep_export!": true, - "description": "The type for an authorization server." + "__bicep_export!": true } }, - "diagnosticSettingFullType": { + "privateLinkServiceConnectionType": { "type": "object", "properties": { "name": { "type": "string", - "nullable": true, "metadata": { - "description": "Optional. The name of the diagnostic setting." + "description": "Required. The name of the private link service connection." } }, - "logCategoriesAndGroups": { - "type": "array", - "items": { - "type": "object", - "properties": { - "category": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." - } - }, - "categoryGroup": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." - } + "properties": { + "type": "object", + "properties": { + "groupIds": { + "type": "array", + "items": { + "type": "string" }, - "enabled": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Enable or disable the category explicitly. Default is `true`." - } + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." - } - }, - "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`." - } + }, + "privateLinkServiceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of private link service." + } + }, + "requestMessage": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." } } }, - "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." + "description": "Required. Properties of private link service connection." } - }, - "eventHubName": { + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "customDnsConfigType": { + "type": "object", + "properties": { + "fqdn": { "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." + "description": "Optional. FQDN that resolves to private endpoint IP address." } }, - "marketplacePartnerResourceId": { - "type": "string", - "nullable": true, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, "metadata": { - "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + "description": "Required. A list of private IP addresses of the private endpoint." } } }, "metadata": { - "description": "An AVM-aligned type for a diagnostic setting. To be used if both logs & metrics are supported by the resource provider.", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.1" - } + "__bicep_export!": true } }, "lockType": { @@ -62432,35 +74539,30 @@ "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.4.1" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, - "managedIdentityAllType": { + "privateDnsZoneGroupConfigType": { "type": "object", "properties": { - "systemAssigned": { - "type": "bool", + "name": { + "type": "string", "nullable": true, "metadata": { - "description": "Optional. Enables system assigned managed identity on the resource." + "description": "Optional. The name of the private DNS zone group config." } }, - "userAssignedResourceIds": { - "type": "array", - "items": { - "type": "string" - }, - "nullable": true, + "privateDnsZoneResourceId": { + "type": "string", "metadata": { - "description": "Optional. The resource ID(s) to assign to the resource. Required if a user assigned identity is used for encryption." + "description": "Required. The resource id of the private DNS zone." } } }, "metadata": { - "description": "An AVM-aligned type for a managed identity configuration. To be used if both a system-assigned & user-assigned identities are supported by the resource provider.", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.1" + "sourceTemplate": "private-dns-zone-group/main.bicep" } } }, @@ -62533,348 +74635,126 @@ } }, "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.4.1" - } - } - }, - "tokenBodyParameterType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. Body parameter name." - } - }, - "value": { - "type": "string", - "metadata": { - "description": "Required. Body parameter value." - } - } - }, - "metadata": { - "description": "The type for a token body parameter.", - "__bicep_imported_from!": { - "sourceTemplate": "authorization-server/main.bicep" - } - } - } - }, - "parameters": { - "additionalLocations": { - "type": "array", - "defaultValue": [], - "metadata": { - "description": "Optional. Additional datacenter locations of the API Management service. Not supported with V2 SKUs." - } - }, - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the API Management service." - } - }, - "certificates": { - "type": "array", - "defaultValue": [], - "maxLength": 10, - "metadata": { - "description": "Optional. List of Certificates that need to be installed in the API Management service. Max supported certificates that can be installed is 10." - } - }, - "enableTelemetry": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Optional. Enable/Disable usage telemetry for module." - } - }, - "customProperties": { - "type": "object", - "defaultValue": { - "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TripleDes168": "False", - "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_CBC_SHA": "False", - "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_256_CBC_SHA": "False", - "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_CBC_SHA256": "False", - "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA": "False", - "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_256_CBC_SHA256": "False", - "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA": "False", - "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_GCM_SHA256": "False" - }, - "metadata": { - "description": "Optional. Custom properties of the API Management service. Not supported if SKU is Consumption." - } - }, - "disableGateway": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "Optional. Property only valid for an API Management service deployed in multiple locations. This can be used to disable the gateway in master region." - } - }, - "enableClientCertificate": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "Optional. Property only meant to be used for Consumption SKU Service. This enforces a client certificate to be presented on each request to the gateway. This also enables the ability to authenticate the certificate in the policy on the gateway." - } - }, - "hostnameConfigurations": { - "type": "array", - "defaultValue": [], - "metadata": { - "description": "Optional. Custom hostname configuration of the API Management service." - } - }, - "managedIdentities": { - "$ref": "#/definitions/managedIdentityAllType", - "nullable": true, - "metadata": { - "description": "Optional. The managed identity definition for this resource." - } - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]", - "metadata": { - "description": "Optional. Location for all Resources." - } - }, - "lock": { - "$ref": "#/definitions/lockType", - "nullable": true, - "metadata": { - "description": "Optional. The lock settings of the service." - } - }, - "minApiVersion": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Limit control plane API calls to API Management service with version equal to or newer than this value." - } - }, - "notificationSenderEmail": { - "type": "string", - "defaultValue": "apimgmt-noreply@mail.windowsazure.com", - "metadata": { - "description": "Optional. The notification sender email address for the service." - } - }, - "publisherEmail": { - "type": "string", - "metadata": { - "description": "Required. The email address of the owner of the service." - } - }, - "publisherName": { - "type": "string", - "metadata": { - "description": "Required. The name of the owner of the service." - } - }, - "restore": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "Optional. Undelete API Management Service if it was previously soft-deleted. If this flag is specified and set to True all other properties will be ignored." - } - }, - "roleAssignments": { - "type": "array", - "items": { - "$ref": "#/definitions/roleAssignmentType" - }, - "nullable": true, - "metadata": { - "description": "Optional. Array of role assignments to create." + "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.5.1" + } } - }, - "sku": { + } + }, + "parameters": { + "name": { "type": "string", - "defaultValue": "Premium", - "allowedValues": [ - "Consumption", - "Developer", - "Basic", - "Standard", - "Premium", - "StandardV2", - "BasicV2" - ], - "metadata": { - "description": "Optional. The pricing tier of this API Management service." - } - }, - "skuCapacity": { - "type": "int", - "defaultValue": 2, "metadata": { - "description": "Conditional. The scale units for this API Management service. Required if using Basic, Standard, or Premium skus. For range of capacities for each sku, reference https://azure.microsoft.com/en-us/pricing/details/api-management/." + "description": "Required. Name of the private endpoint resource to create." } }, "subnetResourceId": { "type": "string", - "nullable": true, "metadata": { - "description": "Optional. The full resource ID of a subnet in a virtual network to deploy the API Management service in." + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." } }, - "tags": { - "type": "object", + "applicationSecurityGroupResourceIds": { + "type": "array", + "items": { + "type": "string" + }, "nullable": true, "metadata": { - "description": "Optional. Tags of the resource." + "description": "Optional. Application security groups in which the private endpoint IP configuration is included." } }, - "virtualNetworkType": { + "customNetworkInterfaceName": { "type": "string", - "defaultValue": "None", - "allowedValues": [ - "None", - "External", - "Internal" - ], + "nullable": true, "metadata": { - "description": "Optional. The type of VPN in which API Management service needs to be configured in. None (Default Value) means the API Management service is not part of any Virtual Network, External means the API Management deployment is set up inside a Virtual Network having an internet Facing Endpoint, and Internal means that API Management deployment is setup inside a Virtual Network having an Intranet Facing Endpoint only." + "description": "Optional. The custom name of the network interface attached to the private endpoint." } }, - "diagnosticSettings": { + "ipConfigurations": { "type": "array", "items": { - "$ref": "#/definitions/diagnosticSettingFullType" + "$ref": "#/definitions/ipConfigurationType" }, "nullable": true, "metadata": { - "description": "Optional. The diagnostic settings of the service." + "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." } }, - "zones": { - "type": "array", - "defaultValue": [ - 1, - 2 - ], + "privateDnsZoneGroup": { + "$ref": "#/definitions/privateDnsZoneGroupType", + "nullable": true, "metadata": { - "description": "Optional. A list of availability zones denoting where the resource needs to come from. Only supported by Premium sku." + "description": "Optional. The private DNS zone group to configure for the private endpoint." } }, - "newGuidValue": { + "location": { "type": "string", - "defaultValue": "[newGuid()]", - "metadata": { - "description": "Optional. Necessary to create a new GUID." - } - }, - "apis": { - "type": "array", - "defaultValue": [], + "defaultValue": "[resourceGroup().location]", "metadata": { - "description": "Optional. APIs." + "description": "Optional. Location for all Resources." } }, - "apiVersionSets": { - "type": "array", - "defaultValue": [], + "lock": { + "$ref": "#/definitions/lockType", + "nullable": true, "metadata": { - "description": "Optional. API Version Sets." + "description": "Optional. The lock settings of the service." } }, - "authorizationServers": { + "roleAssignments": { "type": "array", "items": { - "$ref": "#/definitions/authorizationServerType" + "$ref": "#/definitions/roleAssignmentType" }, "nullable": true, "metadata": { - "description": "Optional. Authorization servers." - } - }, - "backends": { - "type": "array", - "defaultValue": [], - "metadata": { - "description": "Optional. Backends." - } - }, - "caches": { - "type": "array", - "defaultValue": [], - "metadata": { - "description": "Optional. Caches." - } - }, - "apiDiagnostics": { - "type": "array", - "defaultValue": [], - "metadata": { - "description": "Optional. API Diagnostics." - } - }, - "identityProviders": { - "type": "array", - "defaultValue": [], - "metadata": { - "description": "Optional. Identity providers." - } - }, - "loggers": { - "type": "array", - "defaultValue": [], - "metadata": { - "description": "Optional. Loggers." - } - }, - "namedValues": { - "type": "array", - "defaultValue": [], - "metadata": { - "description": "Optional. Named values." + "description": "Optional. Array of role assignments to create." } }, - "policies": { - "type": "array", - "defaultValue": [], + "tags": { + "type": "object", + "nullable": true, "metadata": { - "description": "Optional. Policies." + "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." } }, - "portalsettings": { + "customDnsConfigs": { "type": "array", - "defaultValue": [], + "items": { + "$ref": "#/definitions/customDnsConfigType" + }, + "nullable": true, "metadata": { - "description": "Optional. Portal settings." + "description": "Optional. Custom DNS configurations." } }, - "products": { + "manualPrivateLinkServiceConnections": { "type": "array", - "defaultValue": [], + "items": { + "$ref": "#/definitions/privateLinkServiceConnectionType" + }, + "nullable": true, "metadata": { - "description": "Optional. Products." + "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." } }, - "subscriptions": { + "privateLinkServiceConnections": { "type": "array", - "defaultValue": [], - "metadata": { - "description": "Optional. Subscriptions." - } - }, - "publicIpAddressResourceId": { - "type": "string", + "items": { + "$ref": "#/definitions/privateLinkServiceConnectionType" + }, "nullable": true, "metadata": { - "description": "Optional. Public Standard SKU IP V4 based IP address to be associated with Virtual Network deployed service in the region. Supported only for Developer and Premium SKU being deployed in Virtual Network." + "description": "Conditional. A grouping of information about the connection to the remote resource. Required if `manualPrivateLinkServiceConnections` is empty." } }, - "enableDeveloperPortal": { + "enableTelemetry": { "type": "bool", - "defaultValue": false, + "defaultValue": true, "metadata": { - "description": "Optional. Enable the Developer Portal. The developer portal is not supported on the Consumption SKU." + "description": "Optional. Enable/Disable usage telemetry for module." } } }, @@ -62886,18 +74766,17 @@ "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)))))]" } ], - "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", - "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned,UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', 'None')), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", "builtInRoleNames": { - "API Management Developer Portal Content Editor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c031e6a8-4391-4de0-8d69-4706a7ed3729')]", - "API Management Service Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '312a565d-c81f-4fd8-895a-4e21e48d571c')]", - "API Management Service Operator Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e022efe7-f5ba-4159-bbe4-b44f577e9b61')]", - "API Management Service Reader Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '71522526-b88f-4d52-b57f-d31fc3546d0d')]", "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')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" } }, "resources": { @@ -62905,7 +74784,7 @@ "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2024-03-01", - "name": "[format('46d3xbcp.res.apimanagement-service.{0}.{1}', replace('0.9.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.11.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -62921,100 +74800,55 @@ } } }, - "service": { - "type": "Microsoft.ApiManagement/service", + "privateEndpoint": { + "type": "Microsoft.Network/privateEndpoints", "apiVersion": "2024-05-01", "name": "[parameters('name')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", - "sku": { - "name": "[parameters('sku')]", - "capacity": "[if(contains(parameters('sku'), 'Consumption'), 0, if(contains(parameters('sku'), 'Developer'), 1, parameters('skuCapacity')))]" - }, - "zones": "[if(contains(parameters('sku'), 'Premium'), parameters('zones'), createArray())]", - "identity": "[variables('identity')]", "properties": { - "publisherEmail": "[parameters('publisherEmail')]", - "publisherName": "[parameters('publisherName')]", - "notificationSenderEmail": "[parameters('notificationSenderEmail')]", - "hostnameConfigurations": "[parameters('hostnameConfigurations')]", - "additionalLocations": "[if(contains(parameters('sku'), 'Premium'), parameters('additionalLocations'), createArray())]", - "customProperties": "[if(contains(parameters('sku'), 'Consumption'), null(), parameters('customProperties'))]", - "certificates": "[parameters('certificates')]", - "enableClientCertificate": "[if(parameters('enableClientCertificate'), true(), null())]", - "disableGateway": "[parameters('disableGateway')]", - "virtualNetworkType": "[parameters('virtualNetworkType')]", - "virtualNetworkConfiguration": "[if(not(empty(parameters('subnetResourceId'))), createObject('subnetResourceId', parameters('subnetResourceId')), null())]", - "publicIpAddressId": "[if(not(empty(parameters('publicIpAddressResourceId'))), parameters('publicIpAddressResourceId'), null())]", - "apiVersionConstraint": "[if(not(empty(parameters('minApiVersion'))), createObject('minApiVersion', parameters('minApiVersion')), createObject('minApiVersion', '2021-08-01'))]", - "restore": "[parameters('restore')]", - "developerPortalStatus": "[if(not(equals(parameters('sku'), 'Consumption')), if(parameters('enableDeveloperPortal'), 'Enabled', 'Disabled'), null())]" + "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')]" + } } }, - "service_lock": { + "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.ApiManagement/service/{0}', parameters('name'))]", + "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": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" }, "dependsOn": [ - "service" - ] - }, - "service_diagnosticSettings": { - "copy": { - "name": "service_diagnosticSettings", - "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" - }, - "type": "Microsoft.Insights/diagnosticSettings", - "apiVersion": "2021-05-01-preview", - "scope": "[format('Microsoft.ApiManagement/service/{0}', parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", - "properties": { - "copy": [ - { - "name": "metrics", - "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", - "input": { - "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", - "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", - "timeGrain": null - } - }, - { - "name": "logs", - "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", - "input": { - "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", - "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", - "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" - } - } - ], - "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", - "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", - "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", - "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", - "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", - "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" - }, - "dependsOn": [ - "service" + "privateEndpoint" ] }, - "service_roleAssignments": { + "privateEndpoint_roleAssignments": { "copy": { - "name": "service_roleAssignments", + "name": "privateEndpoint_roleAssignments", "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.ApiManagement/service/{0}', parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.ApiManagement/service', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "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]", @@ -63025,91 +74859,28 @@ "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" }, "dependsOn": [ - "service" + "privateEndpoint" ] }, - "service_apis": { - "copy": { - "name": "service_apis", - "count": "[length(parameters('apis'))]" - }, + "privateEndpoint_privateDnsZoneGroup": { + "condition": "[not(empty(parameters('privateDnsZoneGroup')))]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-Apim-Api-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "name": "[format('{0}-PrivateEndpoint-PrivateDnsZoneGroup', uniqueString(deployment().name))]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "apiManagementServiceName": { - "value": "[parameters('name')]" - }, - "displayName": { - "value": "[parameters('apis')[copyIndex()].displayName]" - }, "name": { - "value": "[parameters('apis')[copyIndex()].name]" - }, - "path": { - "value": "[parameters('apis')[copyIndex()].path]" - }, - "apiDescription": { - "value": "[tryGet(parameters('apis')[copyIndex()], 'apiDescription')]" - }, - "apiRevision": { - "value": "[tryGet(parameters('apis')[copyIndex()], 'apiRevision')]" - }, - "apiRevisionDescription": { - "value": "[tryGet(parameters('apis')[copyIndex()], 'apiRevisionDescription')]" - }, - "apiType": { - "value": "[tryGet(parameters('apis')[copyIndex()], 'apiType')]" - }, - "apiVersion": { - "value": "[tryGet(parameters('apis')[copyIndex()], 'apiVersion')]" - }, - "apiVersionDescription": { - "value": "[tryGet(parameters('apis')[copyIndex()], 'apiVersionDescription')]" - }, - "apiVersionSetId": { - "value": "[tryGet(parameters('apis')[copyIndex()], 'apiVersionSetId')]" - }, - "authenticationSettings": { - "value": "[tryGet(parameters('apis')[copyIndex()], 'authenticationSettings')]" - }, - "format": { - "value": "[tryGet(parameters('apis')[copyIndex()], 'format')]" - }, - "isCurrent": { - "value": "[tryGet(parameters('apis')[copyIndex()], 'isCurrent')]" - }, - "protocols": { - "value": "[tryGet(parameters('apis')[copyIndex()], 'protocols')]" - }, - "policies": { - "value": "[tryGet(parameters('apis')[copyIndex()], 'policies')]" - }, - "serviceUrl": { - "value": "[tryGet(parameters('apis')[copyIndex()], 'serviceUrl')]" - }, - "sourceApiId": { - "value": "[tryGet(parameters('apis')[copyIndex()], 'sourceApiId')]" - }, - "subscriptionKeyParameterNames": { - "value": "[tryGet(parameters('apis')[copyIndex()], 'subscriptionKeyParameterNames')]" - }, - "subscriptionRequired": { - "value": "[tryGet(parameters('apis')[copyIndex()], 'subscriptionRequired')]" - }, - "type": { - "value": "[tryGet(parameters('apis')[copyIndex()], 'type')]" + "value": "[tryGet(parameters('privateDnsZoneGroup'), 'name')]" }, - "value": { - "value": "[tryGet(parameters('apis')[copyIndex()], 'value')]" + "privateEndpointName": { + "value": "[parameters('name')]" }, - "wsdlSelector": { - "value": "[tryGet(parameters('apis')[copyIndex()], 'wsdlSelector')]" + "privateDnsZoneConfigs": { + "value": "[parameters('privateDnsZoneGroup').privateDnsZoneGroupConfigs]" } }, "template": { @@ -63119,1255 +74890,1469 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.34.1.11899", - "templateHash": "7731867308363152438" - }, - "name": "API Management Service APIs", - "description": "This module deploys an API Management Service API." - }, - "parameters": { - "name": { - "type": "string", - "metadata": { - "description": "Required. API revision identifier. Must be unique in the current API Management service instance. Non-current revision has ;rev=n as a suffix where n is the revision number." - } - }, - "policies": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. Array of Policies to apply to the Service API." - } - }, - "diagnostics": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. Array of diagnostics to apply to the Service API." - } - }, - "apiManagementServiceName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent API Management service. Required if the template is used in a standalone deployment." - } - }, - "apiRevision": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Describes the Revision of the API. If no value is provided, default revision 1 is created." - } - }, - "apiRevisionDescription": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Description of the API Revision." - } - }, - "apiType": { - "type": "string", - "defaultValue": "http", - "allowedValues": [ - "graphql", - "http", - "soap", - "websocket" - ], - "metadata": { - "description": "Optional. Type of API to create. * http creates a REST API * soap creates a SOAP pass-through API * websocket creates websocket API * graphql creates GraphQL API." - } - }, - "apiVersion": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Indicates the Version identifier of the API if the API is versioned." - } - }, - "apiVersionSetId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Indicates the Version identifier of the API version set." - } - }, - "apiVersionDescription": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Description of the API Version." - } - }, - "authenticationSettings": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Collection of authentication settings included into this API." - } - }, - "apiDescription": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Description of the API. May include HTML formatting tags." - } - }, - "displayName": { - "type": "string", - "maxLength": 300, - "metadata": { - "description": "Required. API name. Must be 1 to 300 characters long." - } - }, - "format": { - "type": "string", - "defaultValue": "openapi", - "allowedValues": [ - "wadl-xml", - "wadl-link-json", - "swagger-json", - "swagger-link-json", - "wsdl", - "wsdl-link", - "openapi", - "openapi+json", - "openapi-link", - "openapi+json-link" - ], - "metadata": { - "description": "Optional. Format of the Content in which the API is getting imported." - } - }, - "isCurrent": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Optional. Indicates if API revision is current API revision." - } + "version": "0.34.44.8038", + "templateHash": "13997305779829540948" }, - "loggerName": { - "type": "string", - "defaultValue": "", + "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": { - "description": "Conditional. The name of the API management service logger. Required if using api/diagnostics." + "__bicep_export!": true } - }, - "path": { + } + }, + "parameters": { + "privateEndpointName": { "type": "string", "metadata": { - "description": "Required. Relative URL uniquely identifying this API and all of its resource paths within the API Management service instance. It is appended to the API endpoint base URL specified during the service instance creation to form a public URL for this API." + "description": "Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment." } }, - "protocols": { + "privateDnsZoneConfigs": { "type": "array", - "defaultValue": [ - "https" - ], + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, + "minLength": 1, + "maxLength": 5, "metadata": { - "description": "Optional. Describes on which protocols the operations in this API can be invoked. - HTTP or HTTPS." + "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." } }, - "serviceUrl": { + "name": { "type": "string", - "nullable": true, - "maxLength": 2000, + "defaultValue": "default", "metadata": { - "description": "Optional. Absolute URL of the backend service implementing this API. Cannot be more than 2000 characters long." + "description": "Optional. The name of the private DNS zone group." } - }, - "sourceApiId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. API identifier of the source API." + } + }, + "variables": { + "copy": [ + { + "name": "privateDnsZoneConfigsVar", + "count": "[length(parameters('privateDnsZoneConfigs'))]", + "input": { + "name": "[coalesce(tryGet(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')], 'name'), last(split(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId, '/')))]", + "properties": { + "privateDnsZoneId": "[parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId]" + } + } } + ] + }, + "resources": { + "privateEndpoint": { + "existing": true, + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2024-05-01", + "name": "[parameters('privateEndpointName')]" }, - "subscriptionKeyParameterNames": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Protocols over which API is made available." + "privateDnsZoneGroup": { + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2024-05-01", + "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", + "properties": { + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" } - }, - "subscriptionRequired": { - "type": "bool", - "defaultValue": false, + } + }, + "outputs": { + "name": { + "type": "string", "metadata": { - "description": "Optional. Specifies whether an API or Product subscription is required for accessing the API." - } + "description": "The name of the private endpoint DNS zone group." + }, + "value": "[parameters('name')]" }, - "type": { + "resourceId": { "type": "string", - "defaultValue": "http", - "allowedValues": [ - "graphql", - "http", - "soap", - "websocket" - ], "metadata": { - "description": "Optional. Type of API." - } + "description": "The resource ID of the private endpoint DNS zone group." + }, + "value": "[resourceId('Microsoft.Network/privateEndpoints/privateDnsZoneGroups', parameters('privateEndpointName'), parameters('name'))]" }, - "value": { + "resourceGroupName": { "type": "string", - "nullable": true, "metadata": { - "description": "Optional. Content value when Importing an API." - } - }, - "wsdlSelector": { - "type": "object", - "nullable": true, + "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-05-01', 'full').location]" + }, + "customDnsConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/customDnsConfigType" + }, + "metadata": { + "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": [ + "[resourceId('Microsoft.Resources/deployments', 'private-dns-apim-deployment')]", + "[resourceId('Microsoft.Resources/deployments', take(format('{0}-apim-deployment', parameters('name')), 64))]" + ] + } + ], + "outputs": { + "resourceId": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', take(format('{0}-apim-deployment', parameters('name')), 64)), '2022-09-01').outputs.resourceId.value]" + }, + "name": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', take(format('{0}-apim-deployment', parameters('name')), 64)), '2022-09-01').outputs.name.value]" + }, + "privateEndpointId": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', take(format('{0}-apim-private-endpoint-deployment', parameters('name')), 64)), '2022-09-01').outputs.resourceId.value]" + }, + "privateEndpointName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', take(format('{0}-apim-private-endpoint-deployment', parameters('name')), 64)), '2022-09-01').outputs.name.value]" + } + } + } + }, + "dependsOn": [ + "logAnalyticsWorkspace", + "network" + ] + }, + "cosmosDb": { + "condition": "[parameters('cosmosDbEnabled')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('{0}-cosmosdb-deployment', parameters('name')), 64)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[format('cos{0}{1}', parameters('name'), variables('resourceToken'))]" + }, + "location": { + "value": "[parameters('location')]" + }, + "networkIsolation": { + "value": "[parameters('networkIsolation')]" + }, + "virtualNetworkResourceId": "[if(parameters('networkIsolation'), createObject('value', reference('network').outputs.resourceId.value), createObject('value', ''))]", + "virtualNetworkSubnetResourceId": "[if(parameters('networkIsolation'), createObject('value', reference('network').outputs.defaultSubnetResourceId.value), createObject('value', ''))]", + "logAnalyticsWorkspaceResourceId": { + "value": "[reference('logAnalyticsWorkspace').outputs.resourceId.value]" + }, + "databases": { + "value": "[parameters('cosmosDatabases')]" + }, + "sqlRoleAssignmentsPrincipalIds": "[if(variables('deploySampleApp'), createObject('value', createArray(reference('appIdentity').outputs.principalId.value)), createObject('value', createArray()))]", + "tags": { + "value": "[variables('allTags')]" + } + }, + "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.1.42791", + "templateHash": "14492739760745359867" + } + }, + "definitions": { + "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.5.1" + } + } + }, + "sqlDatabaseType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the SQL database ." + } + }, + "throughput": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Default to 400. Request units per second. Will be ignored if autoscaleSettingsMaxThroughput is used. Setting throughput at the database level is only recommended for development/test or when workload across all containers in the shared throughput database is uniform. For best performance for large production workloads, it is recommended to set dedicated throughput (autoscale or manual) at the container level and not at the database level." + } + }, + "autoscaleSettingsMaxThroughput": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Specifies the Autoscale settings and represents maximum throughput, the resource can scale up to. The autoscale throughput should have valid throughput values between 1000 and 1000000 inclusive in increments of 1000. If value is set to null, then autoscale will be disabled. Setting throughput at the database level is only recommended for development/test or when workload across all containers in the shared throughput database is uniform. For best performance for large production workloads, it is recommended to set dedicated throughput (autoscale or manual) at the container level and not at the database level." + } + }, + "containers": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the container." + } + }, + "paths": { + "type": "array", + "items": { + "type": "string" + }, + "minLength": 1, + "maxLength": 3, + "metadata": { + "description": "Required. List of paths using which data within the container can be partitioned. For kind=MultiHash it can be up to 3. For anything else it needs to be exactly 1." + } + }, + "analyticalStorageTtl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Default to 0. Indicates how long data should be retained in the analytical store, for a container. Analytical store is enabled when ATTL is set with a value other than 0. If the value is set to -1, the analytical store retains all historical data, irrespective of the retention of the data in the transactional store." + } + }, + "autoscaleSettingsMaxThroughput": { + "type": "int", + "nullable": true, + "maxValue": 1000000, + "metadata": { + "description": "Optional. Specifies the Autoscale settings and represents maximum throughput, the resource can scale up to. The autoscale throughput should have valid throughput values between 1000 and 1000000 inclusive in increments of 1000. If value is set to null, then autoscale will be disabled. For best performance for large production workloads, it is recommended to set dedicated throughput (autoscale or manual) at the container level." + } + }, + "conflictResolutionPolicy": { + "type": "object", + "properties": { + "conflictResolutionPath": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The conflict resolution path in the case of LastWriterWins mode. Required if `mode` is set to 'LastWriterWins'." + } + }, + "conflictResolutionProcedure": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The procedure to resolve conflicts in the case of custom mode. Required if `mode` is set to 'Custom'." + } + }, + "mode": { + "type": "string", + "allowedValues": [ + "Custom", + "LastWriterWins" + ], + "metadata": { + "description": "Required. Indicates the conflict resolution mode." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The conflict resolution policy for the container. Conflicts and conflict resolution policies are applicable if the Azure Cosmos DB account is configured with multiple write regions." + } + }, + "defaultTtl": { + "type": "int", + "nullable": true, + "minValue": -1, + "maxValue": 2147483647, + "metadata": { + "description": "Optional. Default to -1. Default time to live (in seconds). With Time to Live or TTL, Azure Cosmos DB provides the ability to delete items automatically from a container after a certain time period. If the value is set to \"-1\", it is equal to infinity, and items don't expire by default." + } + }, + "indexingPolicy": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Indexing policy of the container." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "Hash", + "MultiHash" + ], + "nullable": true, + "metadata": { + "description": "Optional. Default to Hash. Indicates the kind of algorithm used for partitioning." + } + }, + "version": { + "type": "int", + "allowedValues": [ + 1, + 2 + ], + "nullable": true, + "metadata": { + "description": "Optional. Default to 1 for Hash and 2 for MultiHash - 1 is not allowed for MultiHash. Version of the partition key definition." + } + }, + "throughput": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Default to 400. Request Units per second. Will be ignored if autoscaleSettingsMaxThroughput is used." + } + }, + "uniqueKeyPolicyKeys": { + "type": "array", + "items": { + "type": "object", + "properties": { + "paths": { + "type": "array", + "items": { + "type": "string" + }, "metadata": { - "description": "Optional. Criteria to limit import of WSDL to a subset of the document." + "description": "Required. List of paths must be unique for each document in the Azure Cosmos DB service." } } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The unique key policy configuration containing a list of unique keys that enforces uniqueness constraint on documents in the collection in the Azure Cosmos DB service." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of containers to deploy in the SQL database." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "customTypes.bicep" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Name of the Cosmos DB Account." + } + }, + "location": { + "type": "string", + "metadata": { + "description": "Specifies the location for all the Azure resources." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Tags to be applied to the resources." + } + }, + "virtualNetworkResourceId": { + "type": "string", + "metadata": { + "description": "Resource ID of the virtual network to link the private DNS zones." + } + }, + "virtualNetworkSubnetResourceId": { + "type": "string", + "metadata": { + "description": "Resource ID of the subnet for the private endpoint." + } + }, + "logAnalyticsWorkspaceResourceId": { + "type": "string", + "metadata": { + "description": "Resource ID of the Log Analytics workspace to use for diagnostic settings." + } + }, + "networkIsolation": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Specifies whether network isolation is enabled. This will create a private endpoint for the Cosmos DB Account and link the private DNS zone." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "sqlRoleAssignmentsPrincipalIds": { + "type": "array", + "items": { + "type": "string" + }, + "defaultValue": [], + "metadata": { + "description": "Optional. List of principal IDs for the custom read/write SQL role assignment." + } + }, + "databases": { + "type": "array", + "items": { + "$ref": "#/definitions/sqlDatabaseType" + }, + "nullable": true, + "metadata": { + "description": "Optional. List of Cosmos DB databases to deploy." + } + } + }, + "variables": { + "nameFormatted": "[toLower(parameters('name'))]" + }, + "resources": { + "privateDnsZone": { + "condition": "[parameters('networkIsolation')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "private-dns-cosmosdb-deployment", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "privatelink.documents.azure.com" + }, + "virtualNetworkLinks": { + "value": [ + { + "virtualNetworkResourceId": "[parameters('virtualNetworkResourceId')]" + } + ] + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "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.32.4.45862", + "templateHash": "83178825086050429" + }, + "name": "Private DNS Zones", + "description": "This module deploys a Private DNS zone.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "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." + } }, - "resources": { - "service": { - "existing": true, - "type": "Microsoft.ApiManagement/service", - "apiVersion": "2023-05-01-preview", - "name": "[parameters('apiManagementServiceName')]" - }, - "api": { - "type": "Microsoft.ApiManagement/service/apis", - "apiVersion": "2022-08-01", - "name": "[format('{0}/{1}', parameters('apiManagementServiceName'), parameters('name'))]", - "properties": { - "apiRevision": "[parameters('apiRevision')]", - "apiRevisionDescription": "[parameters('apiRevisionDescription')]", - "apiType": "[parameters('apiType')]", - "apiVersion": "[parameters('apiVersion')]", - "apiVersionDescription": "[parameters('apiVersionDescription')]", - "apiVersionSetId": "[parameters('apiVersionSetId')]", - "authenticationSettings": "[coalesce(parameters('authenticationSettings'), createObject())]", - "description": "[coalesce(parameters('apiDescription'), '')]", - "displayName": "[parameters('displayName')]", - "format": "[if(not(empty(parameters('value'))), parameters('format'), null())]", - "isCurrent": "[parameters('isCurrent')]", - "path": "[parameters('path')]", - "protocols": "[parameters('protocols')]", - "serviceUrl": "[parameters('serviceUrl')]", - "sourceApiId": "[parameters('sourceApiId')]", - "subscriptionKeyParameterNames": "[parameters('subscriptionKeyParameterNames')]", - "subscriptionRequired": "[parameters('subscriptionRequired')]", - "type": "[parameters('type')]", - "value": "[parameters('value')]", - "wsdlSelector": "[coalesce(parameters('wsdlSelector'), createObject())]" - } - }, - "policy": { - "copy": { - "name": "policy", - "count": "[length(coalesce(parameters('policies'), createArray()))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-Policy-{1}', deployment().name, copyIndex())]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "apiManagementServiceName": { - "value": "[parameters('apiManagementServiceName')]" - }, - "apiName": { - "value": "[parameters('name')]" - }, - "format": { - "value": "[coalesce(tryGet(coalesce(parameters('policies'), createArray())[copyIndex()], 'format'), 'xml')]" - }, - "value": { - "value": "[coalesce(parameters('policies'), createArray())[copyIndex()].value]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.34.1.11899", - "templateHash": "3121557432730960376" - }, - "name": "API Management Service APIs Policies", - "description": "This module deploys an API Management Service API Policy." - }, - "parameters": { - "apiManagementServiceName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent API Management service. Required if the template is used in a standalone deployment." - } - }, - "apiName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent API. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", - "defaultValue": "policy", - "metadata": { - "description": "Optional. The name of the policy." - } - }, - "format": { - "type": "string", - "defaultValue": "xml", - "allowedValues": [ - "rawxml", - "rawxml-link", - "xml", - "xml-link" - ], - "metadata": { - "description": "Optional. Format of the policyContent." - } - }, - "value": { - "type": "string", - "metadata": { - "description": "Required. Contents of the Policy as defined by the format." - } - } - }, - "resources": [ - { - "type": "Microsoft.ApiManagement/service/apis/policies", - "apiVersion": "2022-08-01", - "name": "[format('{0}/{1}/{2}', parameters('apiManagementServiceName'), parameters('apiName'), parameters('name'))]", - "properties": { - "format": "[parameters('format')]", - "value": "[parameters('value')]" - } - } - ], - "outputs": { - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the API policy." - }, - "value": "[resourceId('Microsoft.ApiManagement/service/apis/policies', parameters('apiManagementServiceName'), parameters('apiName'), parameters('name'))]" - }, - "name": { - "type": "string", - "metadata": { - "description": "The name of the API policy." - }, - "value": "[parameters('name')]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group the API policy was deployed into." - }, - "value": "[resourceGroup().name]" - } - } - } - }, - "dependsOn": [ - "api" - ] - }, - "diagnostic": { - "copy": { - "name": "diagnostic", - "count": "[length(coalesce(parameters('diagnostics'), createArray()))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-diagnostics-{1}', deployment().name, copyIndex())]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[tryGet(coalesce(parameters('diagnostics'), createArray())[copyIndex()], 'name')]" - }, - "apiManagementServiceName": { - "value": "[parameters('apiManagementServiceName')]" - }, - "apiName": { - "value": "[parameters('name')]" - }, - "loggerName": { - "value": "[parameters('loggerName')]" - }, - "alwaysLog": { - "value": "[tryGet(coalesce(parameters('diagnostics'), createArray())[copyIndex()], 'alwaysLog')]" - }, - "backend": { - "value": "[tryGet(coalesce(parameters('diagnostics'), createArray())[copyIndex()], 'backend')]" - }, - "frontend": { - "value": "[tryGet(coalesce(parameters('diagnostics'), createArray())[copyIndex()], 'frontend')]" - }, - "httpCorrelationProtocol": { - "value": "[tryGet(coalesce(parameters('diagnostics'), createArray())[copyIndex()], 'httpCorrelationProtocol')]" - }, - "logClientIp": { - "value": "[tryGet(coalesce(parameters('diagnostics'), createArray())[copyIndex()], 'logClientIp')]" - }, - "metrics": { - "value": "[tryGet(coalesce(parameters('diagnostics'), createArray())[copyIndex()], 'metrics')]" - }, - "operationNameFormat": { - "value": "[tryGet(coalesce(parameters('diagnostics'), createArray())[copyIndex()], 'operationNameFormat')]" - }, - "samplingPercentage": { - "value": "[tryGet(coalesce(parameters('diagnostics'), createArray())[copyIndex()], 'samplingPercentage')]" - }, - "verbosity": { - "value": "[tryGet(coalesce(parameters('diagnostics'), createArray())[copyIndex()], 'verbosity')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.34.1.11899", - "templateHash": "220134378763209880" - }, - "name": "API Management Service APIs Diagnostics.", - "description": "This module deploys an API Management Service API Diagnostics." - }, - "parameters": { - "apiManagementServiceName": { - "type": "string", - "metadata": { - "description": "Required. The name of the parent API Management service." - } - }, - "apiName": { - "type": "string", - "metadata": { - "description": "Required. The name of the parent API." - } - }, - "loggerName": { - "type": "string", - "metadata": { - "description": "Required. The name of the logger." - } - }, - "name": { - "type": "string", - "defaultValue": "local", - "allowedValues": [ - "azuremonitor", - "applicationinsights", - "local" - ], - "metadata": { - "description": "Optional. Type of diagnostic resource." - } - }, - "alwaysLog": { - "type": "string", - "defaultValue": "allErrors", - "metadata": { - "description": "Optional. Specifies for what type of messages sampling settings should not apply." - } - }, - "backend": { - "type": "object", - "defaultValue": {}, - "metadata": { - "description": "Optional. Diagnostic settings for incoming/outgoing HTTP messages to the Backend." - } - }, - "frontend": { - "type": "object", - "defaultValue": {}, - "metadata": { - "description": "Optional. Diagnostic settings for incoming/outgoing HTTP messages to the Gateway." - } - }, - "httpCorrelationProtocol": { - "type": "string", - "defaultValue": "Legacy", - "allowedValues": [ - "Legacy", - "None", - "W3C" - ], - "metadata": { - "description": "Conditional. Sets correlation protocol to use for Application Insights diagnostics. Required if using Application Insights." - } - }, - "logClientIp": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "Optional. Log the ClientIP." - } - }, - "metrics": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "Conditional. Emit custom metrics via emit-metric policy. Required if using Application Insights." - } - }, - "operationNameFormat": { - "type": "string", - "defaultValue": "Name", - "allowedValues": [ - "Name", - "URI" - ], - "metadata": { - "description": "Conditional. The format of the Operation Name for Application Insights telemetries. Required if using Application Insights." - } - }, - "samplingPercentage": { - "type": "int", - "defaultValue": 100, - "metadata": { - "description": "Optional. Rate of sampling for fixed-rate sampling. Specifies the percentage of requests that are logged. 0% sampling means zero requests logged, while 100% sampling means all requests logged." - } - }, - "verbosity": { - "type": "string", - "defaultValue": "error", - "allowedValues": [ - "error", - "information", - "verbose" - ], - "metadata": { - "description": "Optional. The verbosity level applied to traces emitted by trace policies." - } - } - }, - "resources": [ - { - "type": "Microsoft.ApiManagement/service/apis/diagnostics", - "apiVersion": "2022-08-01", - "name": "[format('{0}/{1}/{2}', parameters('apiManagementServiceName'), parameters('apiName'), parameters('name'))]", - "properties": { - "alwaysLog": "[parameters('alwaysLog')]", - "backend": "[parameters('backend')]", - "frontend": "[parameters('frontend')]", - "httpCorrelationProtocol": "[parameters('httpCorrelationProtocol')]", - "logClientIp": "[parameters('logClientIp')]", - "loggerId": "[resourceId('Microsoft.ApiManagement/service/loggers', parameters('apiManagementServiceName'), parameters('loggerName'))]", - "metrics": "[parameters('metrics')]", - "operationNameFormat": "[parameters('operationNameFormat')]", - "sampling": { - "percentage": "[parameters('samplingPercentage')]", - "samplingType": "fixed" - }, - "verbosity": "[parameters('verbosity')]" - } - } - ], - "outputs": { - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the API diagnostic." - }, - "value": "[resourceId('Microsoft.ApiManagement/service/apis/diagnostics', parameters('apiManagementServiceName'), parameters('apiName'), parameters('name'))]" - }, - "name": { - "type": "string", - "metadata": { - "description": "The name of the API diagnostic." - }, - "value": "[parameters('name')]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group the API diagnostic was deployed into." - }, - "value": "[resourceGroup().name]" - } - } - } - }, - "dependsOn": [ - "api" - ] + "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'." } }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the API management service API." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the API management service API." - }, - "value": "[resourceId('Microsoft.ApiManagement/service/apis', parameters('apiManagementServiceName'), parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group the API management service API was deployed to." - }, - "value": "[resourceGroup().name]" + "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." } } } }, - "dependsOn": [ - "service", - "service_apiVersionSets" - ] + "nullable": true }, - "service_apiVersionSets": { - "copy": { - "name": "service_apiVersionSets", - "count": "[length(parameters('apiVersionSets'))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-Apim-ApiVersionSet-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "lockType": { + "type": "object", "properties": { - "expressionEvaluationOptions": { - "scope": "inner" + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } }, - "mode": "Incremental", - "parameters": { - "apiManagementServiceName": { - "value": "[parameters('name')]" + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "nullable": true + }, + "aType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } }, + "aRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "ipv4Address": { + "type": "string", + "metadata": { + "description": "Required. The IPv4 address of this A record." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of A records in the record set." + } + } + } + }, + "nullable": true + }, + "aaaaType": { + "type": "array", + "items": { + "type": "object", + "properties": { "name": { - "value": "[parameters('apiVersionSets')[copyIndex()].name]" + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } }, - "properties": { - "value": "[coalesce(tryGet(parameters('apiVersionSets')[copyIndex()], 'properties'), createObject())]" + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "aaaaRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "ipv6Address": { + "type": "string", + "metadata": { + "description": "Required. The IPv6 address of this AAAA record." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of AAAA records in the record set." + } } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", + } + }, + "nullable": true + }, + "cnameType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, "metadata": { - "_generator": { - "name": "bicep", - "version": "0.34.1.11899", - "templateHash": "6536427264308776904" + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "cnameRecord": { + "type": "object", + "properties": { + "cname": { + "type": "string", + "metadata": { + "description": "Required. The canonical name of the CNAME record." + } + } }, - "name": "API Management Service API Version Sets", - "description": "This module deploys an API Management Service API Version Set." + "nullable": true, + "metadata": { + "description": "Optional. The CNAME record in the record set." + } + } + } + }, + "nullable": true + }, + "mxType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } }, - "parameters": { - "apiManagementServiceName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent API Management service. Required if the template is used in a standalone deployment." + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "mxRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "exchange": { + "type": "string", + "metadata": { + "description": "Required. The domain name of the mail host for this MX record." + } + }, + "preference": { + "type": "int", + "metadata": { + "description": "Required. The preference value for this MX record." + } + } } }, - "name": { - "type": "string", - "defaultValue": "default", - "metadata": { - "description": "Optional. API Version set name." + "nullable": true, + "metadata": { + "description": "Optional. The list of MX records in the record set." + } + } + } + }, + "nullable": true + }, + "ptrType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "ptrRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "ptrdname": { + "type": "string", + "metadata": { + "description": "Required. The PTR target domain name for this PTR record." + } + } } }, + "nullable": true, + "metadata": { + "description": "Optional. The list of PTR records in the record set." + } + } + } + }, + "nullable": true + }, + "soaType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "soaRecord": { + "type": "object", "properties": { - "type": "object", - "defaultValue": {}, - "metadata": { - "description": "Optional. API Version set properties." + "email": { + "type": "string", + "metadata": { + "description": "Required. The email contact for this SOA record." + } + }, + "expireTime": { + "type": "int", + "metadata": { + "description": "Required. The expire time for this SOA record." + } + }, + "host": { + "type": "string", + "metadata": { + "description": "Required. The domain name of the authoritative name server for this SOA record." + } + }, + "minimumTtl": { + "type": "int", + "metadata": { + "description": "Required. The minimum value for this SOA record. By convention this is used to determine the negative caching duration." + } + }, + "refreshTime": { + "type": "int", + "metadata": { + "description": "Required. The refresh value for this SOA record." + } + }, + "retryTime": { + "type": "int", + "metadata": { + "description": "Required. The retry time for this SOA record." + } + }, + "serialNumber": { + "type": "int", + "metadata": { + "description": "Required. The serial number for this SOA record." + } } + }, + "nullable": true, + "metadata": { + "description": "Optional. The SOA record in the record set." + } + } + } + }, + "nullable": true + }, + "srvType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." } }, - "resources": [ - { - "type": "Microsoft.ApiManagement/service/apiVersionSets", - "apiVersion": "2022-08-01", - "name": "[format('{0}/{1}', parameters('apiManagementServiceName'), parameters('name'))]", - "properties": "[parameters('properties')]" + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." } - ], - "outputs": { - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the API Version set." - }, - "value": "[resourceId('Microsoft.ApiManagement/service/apiVersionSets', parameters('apiManagementServiceName'), parameters('name'))]" - }, - "name": { - "type": "string", - "metadata": { - "description": "The name of the API Version set." - }, - "value": "[parameters('name')]" + }, + "srvRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "priority": { + "type": "int", + "metadata": { + "description": "Required. The priority value for this SRV record." + } + }, + "weight": { + "type": "int", + "metadata": { + "description": "Required. The weight value for this SRV record." + } + }, + "port": { + "type": "int", + "metadata": { + "description": "Required. The port value for this SRV record." + } + }, + "target": { + "type": "string", + "metadata": { + "description": "Required. The target domain name for this SRV record." + } + } + } }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group the API Version set was deployed into." - }, - "value": "[resourceGroup().name]" + "nullable": true, + "metadata": { + "description": "Optional. The list of SRV records in the record set." } } } }, - "dependsOn": [ - "service" - ] + "nullable": true }, - "service_authorizationServers": { - "copy": { - "name": "service_authorizationServers", - "count": "[length(coalesce(parameters('authorizationServers'), createArray()))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-Apim-AuthorizationServer-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "apiManagementServiceName": { - "value": "[parameters('name')]" - }, + "txtType": { + "type": "array", + "items": { + "type": "object", + "properties": { "name": { - "value": "[coalesce(parameters('authorizationServers'), createArray())[copyIndex()].name]" - }, - "displayName": { - "value": "[coalesce(parameters('authorizationServers'), createArray())[copyIndex()].displayName]" - }, - "authorizationEndpoint": { - "value": "[coalesce(parameters('authorizationServers'), createArray())[copyIndex()].authorizationEndpoint]" - }, - "authorizationMethods": { - "value": "[coalesce(tryGet(coalesce(parameters('authorizationServers'), createArray())[copyIndex()], 'authorizationMethods'), createArray('GET'))]" - }, - "bearerTokenSendingMethods": { - "value": "[coalesce(tryGet(coalesce(parameters('authorizationServers'), createArray())[copyIndex()], 'bearerTokenSendingMethods'), createArray('authorizationHeader'))]" - }, - "clientAuthenticationMethod": { - "value": "[coalesce(tryGet(coalesce(parameters('authorizationServers'), createArray())[copyIndex()], 'clientAuthenticationMethod'), createArray('Basic'))]" - }, - "clientId": { - "value": "[coalesce(parameters('authorizationServers'), createArray())[copyIndex()].clientId]" - }, - "clientSecret": { - "value": "[coalesce(parameters('authorizationServers'), createArray())[copyIndex()].clientSecret]" - }, - "clientRegistrationEndpoint": { - "value": "[coalesce(tryGet(coalesce(parameters('authorizationServers'), createArray())[copyIndex()], 'clientRegistrationEndpoint'), '')]" - }, - "defaultScope": { - "value": "[coalesce(tryGet(coalesce(parameters('authorizationServers'), createArray())[copyIndex()], 'defaultScope'), '')]" - }, - "grantTypes": { - "value": "[coalesce(parameters('authorizationServers'), createArray())[copyIndex()].grantTypes]" - }, - "resourceOwnerPassword": { - "value": "[coalesce(tryGet(coalesce(parameters('authorizationServers'), createArray())[copyIndex()], 'resourceOwnerPassword'), '')]" - }, - "resourceOwnerUsername": { - "value": "[coalesce(tryGet(coalesce(parameters('authorizationServers'), createArray())[copyIndex()], 'resourceOwnerUsername'), '')]" - }, - "serverDescription": { - "value": "[coalesce(tryGet(coalesce(parameters('authorizationServers'), createArray())[copyIndex()], 'serverDescription'), '')]" + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } }, - "supportState": { - "value": "[coalesce(tryGet(coalesce(parameters('authorizationServers'), createArray())[copyIndex()], 'supportState'), false())]" + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } }, - "tokenBodyParameters": { - "value": "[coalesce(tryGet(coalesce(parameters('authorizationServers'), createArray())[copyIndex()], 'tokenBodyParameters'), createArray())]" + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } }, - "tokenEndpoint": { - "value": "[coalesce(tryGet(coalesce(parameters('authorizationServers'), createArray())[copyIndex()], 'tokenEndpoint'), '')]" - } - }, - "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.34.1.11899", - "templateHash": "4005929869264436679" - }, - "name": "API Management Service Authorization Servers", - "description": "This module deploys an API Management Service Authorization Server." + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } }, - "definitions": { - "tokenBodyParameterType": { + "txtRecords": { + "type": "array", + "items": { "type": "object", "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. Body parameter name." - } - }, "value": { - "type": "string", + "type": "array", + "items": { + "type": "string" + }, "metadata": { - "description": "Required. Body parameter value." + "description": "Required. The text value of this TXT record." } } - }, - "metadata": { - "__bicep_export!": true, - "description": "The type for a token body parameter." } + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of TXT records in the record set." + } + } + } + }, + "nullable": true + }, + "virtualNetworkLinkType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "minLength": 1, + "maxLength": 80, + "metadata": { + "description": "Optional. The resource name." } }, - "parameters": { - "name": { - "type": "string", - "metadata": { - "description": "Required. Identifier of the authorization server." - } - }, - "displayName": { - "type": "string", - "maxLength": 50, - "metadata": { - "description": "Required. API Management Service Authorization Servers name. Must be 1 to 50 characters long." - } - }, - "apiManagementServiceName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent API Management service. Required if the template is used in a standalone deployment." - } - }, - "authorizationEndpoint": { - "type": "string", - "metadata": { - "description": "Required. OAuth authorization endpoint. See ." - } - }, - "authorizationMethods": { - "type": "array", - "defaultValue": [ - "GET" - ], - "metadata": { - "description": "Optional. HTTP verbs supported by the authorization endpoint. GET must be always present. POST is optional. - HEAD, OPTIONS, TRACE, GET, POST, PUT, PATCH, DELETE." - } - }, - "bearerTokenSendingMethods": { - "type": "array", - "defaultValue": [ - "authorizationHeader" - ], - "metadata": { - "description": "Optional. Specifies the mechanism by which access token is passed to the API. - authorizationHeader or query." - } - }, - "clientAuthenticationMethod": { - "type": "array", - "defaultValue": [ - "Basic" - ], - "metadata": { - "description": "Optional. Method of authentication supported by the token endpoint of this authorization server. Possible values are Basic and/or Body. When Body is specified, client credentials and other parameters are passed within the request body in the application/x-www-form-urlencoded format. - Basic or Body." - } - }, - "clientId": { - "type": "securestring", - "metadata": { - "description": "Required. Client or app ID registered with this authorization server." - } - }, - "clientRegistrationEndpoint": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Optional. Optional reference to a page where client or app registration for this authorization server is performed. Contains absolute URL to entity being referenced." - } - }, - "clientSecret": { - "type": "securestring", - "metadata": { - "description": "Required. Client or app secret registered with this authorization server. This property will not be filled on 'GET' operations! Use '/listSecrets' POST request to get the value." - } - }, - "defaultScope": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Optional. Access token scope that is going to be requested by default. Can be overridden at the API level. Should be provided in the form of a string containing space-delimited values." - } - }, - "serverDescription": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Optional. Description of the authorization server. Can contain HTML formatting tags." - } - }, - "grantTypes": { - "type": "array", - "items": { - "type": "string" - }, - "allowedValues": [ - "authorizationCode", - "clientCredentials", - "implicit", - "resourceOwnerPassword" - ], - "metadata": { - "description": "Required. Form of an authorization grant, which the client uses to request the access token. - authorizationCode, implicit, resourceOwnerPassword, clientCredentials." - } - }, - "resourceOwnerPassword": { - "type": "securestring", - "defaultValue": "", - "metadata": { - "description": "Optional. Can be optionally specified when resource owner password grant type is supported by this authorization server. Default resource owner password." - } - }, - "resourceOwnerUsername": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Optional. Can be optionally specified when resource owner password grant type is supported by this authorization server. Default resource owner username." - } - }, - "supportState": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "Optional. If true, authorization server will include state parameter from the authorization request to its response. Client may use state parameter to raise protocol security." - } - }, - "tokenBodyParameters": { - "type": "array", - "items": { - "$ref": "#/definitions/tokenBodyParameterType" - }, - "defaultValue": [], - "metadata": { - "description": "Optional. Additional parameters required by the token endpoint of this authorization server represented as an array of JSON objects with name and value string properties." - } - }, - "tokenEndpoint": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Optional. OAuth token endpoint. Contains absolute URI to entity being referenced." - } + "virtualNetworkResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the virtual network to link." } }, - "variables": { - "defaultAuthorizationMethods": [ - "GET" - ], - "setAuthorizationMethods": "[union(parameters('authorizationMethods'), variables('defaultAuthorizationMethods'))]" + "location": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Azure Region where the resource lives." + } }, - "resources": { - "service": { - "existing": true, - "type": "Microsoft.ApiManagement/service", - "apiVersion": "2023-05-01-preview", - "name": "[parameters('apiManagementServiceName')]" - }, - "authorizationServer": { - "type": "Microsoft.ApiManagement/service/authorizationServers", - "apiVersion": "2022-08-01", - "name": "[format('{0}/{1}', parameters('apiManagementServiceName'), parameters('name'))]", - "properties": { - "description": "[parameters('serverDescription')]", - "authorizationMethods": "[variables('setAuthorizationMethods')]", - "clientAuthenticationMethod": "[parameters('clientAuthenticationMethod')]", - "tokenBodyParameters": "[parameters('tokenBodyParameters')]", - "tokenEndpoint": "[parameters('tokenEndpoint')]", - "supportState": "[parameters('supportState')]", - "defaultScope": "[parameters('defaultScope')]", - "bearerTokenSendingMethods": "[parameters('bearerTokenSendingMethods')]", - "resourceOwnerUsername": "[parameters('resourceOwnerUsername')]", - "resourceOwnerPassword": "[parameters('resourceOwnerPassword')]", - "displayName": "[parameters('displayName')]", - "clientRegistrationEndpoint": "[parameters('clientRegistrationEndpoint')]", - "authorizationEndpoint": "[parameters('authorizationEndpoint')]", - "grantTypes": "[parameters('grantTypes')]", - "clientId": "[parameters('clientId')]", - "clientSecret": "[parameters('clientSecret')]" - } + "registrationEnabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Is auto-registration of virtual machine records in the virtual network in the Private DNS zone enabled?." } }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the API management service authorization server." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the API management service authorization server." - }, - "value": "[resourceId('Microsoft.ApiManagement/service/authorizationServers', parameters('apiManagementServiceName'), parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group the API management service authorization server was deployed into." - }, - "value": "[resourceGroup().name]" + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Resource tags." + } + }, + "resolutionPolicy": { + "type": "string", + "allowedValues": [ + "Default", + "NxDomainRedirect" + ], + "nullable": true, + "metadata": { + "description": "Optional. The resolution type of the private-dns-zone fallback machanism." } } } }, - "dependsOn": [ - "service" - ] + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Private DNS zone name." + } + }, + "a": { + "$ref": "#/definitions/aType", + "metadata": { + "description": "Optional. Array of A records." + } + }, + "aaaa": { + "$ref": "#/definitions/aaaaType", + "metadata": { + "description": "Optional. Array of AAAA records." + } + }, + "cname": { + "$ref": "#/definitions/cnameType", + "metadata": { + "description": "Optional. Array of CNAME records." + } + }, + "mx": { + "$ref": "#/definitions/mxType", + "metadata": { + "description": "Optional. Array of MX records." + } + }, + "ptr": { + "$ref": "#/definitions/ptrType", + "metadata": { + "description": "Optional. Array of PTR records." + } + }, + "soa": { + "$ref": "#/definitions/soaType", + "metadata": { + "description": "Optional. Array of SOA records." + } + }, + "srv": { + "$ref": "#/definitions/srvType", + "metadata": { + "description": "Optional. Array of SRV records." + } + }, + "txt": { + "$ref": "#/definitions/txtType", + "metadata": { + "description": "Optional. Array of TXT records." + } + }, + "virtualNetworkLinks": { + "$ref": "#/definitions/virtualNetworkLinkType", + "metadata": { + "description": "Optional. Array of custom objects describing vNet links of the DNS zone. Each object should contain properties 'virtualNetworkResourceId' and 'registrationEnabled'. The 'vnetResourceId' is a resource ID of a vNet to link, 'registrationEnabled' (bool) enables automatic DNS registration in the zone for the linked vNet." + } + }, + "location": { + "type": "string", + "defaultValue": "global", + "metadata": { + "description": "Optional. The location of the PrivateDNSZone. Should be global." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } }, - "service_backends": { - "copy": { - "name": "service_backends", - "count": "[length(parameters('backends'))]" - }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "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')]", + "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": "2022-09-01", - "name": "[format('{0}-Apim-Backend-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privatednszone.{0}.{1}', replace('0.7.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, "mode": "Incremental", - "parameters": { - "apiManagementServiceName": { - "value": "[parameters('name')]" - }, - "url": { - "value": "[parameters('backends')[copyIndex()].url]" - }, - "description": { - "value": "[tryGet(parameters('backends')[copyIndex()], 'description')]" - }, - "credentials": { - "value": "[tryGet(parameters('backends')[copyIndex()], 'credentials')]" - }, - "name": { - "value": "[parameters('backends')[copyIndex()].name]" - }, - "protocol": { - "value": "[tryGet(parameters('backends')[copyIndex()], 'protocol')]" - }, - "proxy": { - "value": "[tryGet(parameters('backends')[copyIndex()], 'proxy')]" - }, - "resourceId": { - "value": "[tryGet(parameters('backends')[copyIndex()], 'resourceId')]" - }, - "serviceFabricCluster": { - "value": "[tryGet(parameters('backends')[copyIndex()], 'serviceFabricCluster')]" - }, - "title": { - "value": "[tryGet(parameters('backends')[copyIndex()], 'title')]" - }, - "tls": { - "value": "[coalesce(tryGet(parameters('backends')[copyIndex()], 'tls'), createObject('validateCertificateChain', true(), 'validateCertificateName', true()))]" - } - }, "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.34.1.11899", - "templateHash": "60104329731493736" - }, - "name": "API Management Service Backends", - "description": "This module deploys an API Management Service Backend." - }, - "parameters": { - "apiManagementServiceName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent API Management service. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", - "metadata": { - "description": "Required. Backend Name." - } - }, - "credentials": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Backend Credentials Contract Properties." - } - }, - "description": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Backend Description." - } - }, - "protocol": { - "type": "string", - "defaultValue": "http", - "metadata": { - "description": "Optional. Backend communication protocol. - http or soap." - } - }, - "proxy": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Backend Proxy Contract Properties." - } - }, - "resourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Management Uri of the Resource in External System. This URL can be the Arm Resource ID of Logic Apps, Function Apps or API Apps." - } - }, - "serviceFabricCluster": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Backend Service Fabric Cluster Properties." - } - }, - "title": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Backend Title." - } - }, - "tls": { - "type": "object", - "defaultValue": { - "validateCertificateChain": false, - "validateCertificateName": false - }, - "metadata": { - "description": "Optional. Backend TLS Properties." - } - }, - "url": { - "type": "string", - "metadata": { - "description": "Required. Runtime URL of the Backend." - } - } - }, - "resources": { - "service": { - "existing": true, - "type": "Microsoft.ApiManagement/service", - "apiVersion": "2023-05-01-preview", - "name": "[parameters('apiManagementServiceName')]" - }, - "backend": { - "type": "Microsoft.ApiManagement/service/backends", - "apiVersion": "2022-08-01", - "name": "[format('{0}/{1}', parameters('apiManagementServiceName'), parameters('name'))]", - "properties": { - "title": "[parameters('title')]", - "description": "[parameters('description')]", - "resourceId": "[parameters('resourceId')]", - "properties": { - "serviceFabricCluster": "[parameters('serviceFabricCluster')]" - }, - "credentials": "[parameters('credentials')]", - "proxy": "[parameters('proxy')]", - "tls": "[parameters('tls')]", - "url": "[parameters('url')]", - "protocol": "[parameters('protocol')]" - } - } - }, + "resources": [], "outputs": { - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the API management service backend." - }, - "value": "[resourceId('Microsoft.ApiManagement/service/backends', parameters('apiManagementServiceName'), parameters('name'))]" - }, - "name": { - "type": "string", - "metadata": { - "description": "The name of the API management service backend." - }, - "value": "[parameters('name')]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group the API management service backend was deployed into." - }, - "value": "[resourceGroup().name]" + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" } } } + } + }, + "privateDnsZone": { + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]" + }, + "privateDnsZone_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/privateDnsZones/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" }, "dependsOn": [ - "service" + "privateDnsZone" ] }, - "service_caches": { + "privateDnsZone_roleAssignments": { "copy": { - "name": "service_caches", - "count": "[length(parameters('caches'))]" + "name": "privateDnsZone_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones', 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": [ + "privateDnsZone" + ] + }, + "privateDnsZone_A": { + "copy": { + "name": "privateDnsZone_A", + "count": "[length(coalesce(parameters('a'), createArray()))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-Apim-Cache-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "name": "[format('{0}-PrivateDnsZone-ARecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "apiManagementServiceName": { + "privateDnsZoneName": { "value": "[parameters('name')]" }, - "description": { - "value": "[tryGet(parameters('caches')[copyIndex()], 'description')]" + "name": { + "value": "[coalesce(parameters('a'), createArray())[copyIndex()].name]" }, - "connectionString": { - "value": "[parameters('caches')[copyIndex()].connectionString]" + "aRecords": { + "value": "[tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'aRecords')]" }, - "name": { - "value": "[parameters('caches')[copyIndex()].name]" + "metadata": { + "value": "[tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'metadata')]" }, - "resourceId": { - "value": "[tryGet(parameters('caches')[copyIndex()], 'resourceId')]" + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'ttl'), 3600)]" }, - "useFromLocation": { - "value": "[parameters('caches')[copyIndex()].useFromLocation]" + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'roleAssignments')]" } }, "template": { @@ -64377,90 +76362,206 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.34.1.11899", - "templateHash": "2189305885354046724" + "version": "0.32.4.45862", + "templateHash": "2531120132215940282" }, - "name": "API Management Service Caches", - "description": "This module deploys an API Management Service Cache." + "name": "Private DNS Zone A record", + "description": "This module deploys a Private DNS Zone A record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "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." + } + } + } + }, + "nullable": true + } }, "parameters": { - "apiManagementServiceName": { + "privateDnsZoneName": { "type": "string", "metadata": { - "description": "Conditional. The name of the parent API Management service. Required if the template is used in a standalone deployment." + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." } }, "name": { "type": "string", "metadata": { - "description": "Required. Identifier of the Cache entity. Cache identifier (should be either 'default' or valid Azure region identifier)." + "description": "Required. The name of the A record." } }, - "connectionString": { - "type": "string", + "aRecords": { + "type": "array", + "nullable": true, "metadata": { - "description": "Required. Runtime connection string to cache. Can be referenced by a named value like so, {{}}." + "description": "Optional. The list of A records in the record set." } }, - "description": { - "type": "string", + "metadata": { + "type": "object", "nullable": true, "metadata": { - "description": "Optional. Cache description." + "description": "Optional. The metadata attached to the record set." } }, - "resourceId": { - "type": "string", - "nullable": true, + "ttl": { + "type": "int", + "defaultValue": 3600, "metadata": { - "description": "Optional. Original uri of entity in external system cache points to." + "description": "Optional. The TTL (time-to-live) of the records in the record set." } }, - "useFromLocation": { - "type": "string", + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", "metadata": { - "description": "Required. Location identifier to use cache from (should be either 'default' or valid Azure region identifier)." + "description": "Optional. Array of role assignments to create." } } }, + "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')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, "resources": { - "service": { + "privateDnsZone": { "existing": true, - "type": "Microsoft.ApiManagement/service", - "apiVersion": "2023-05-01-preview", - "name": "[parameters('apiManagementServiceName')]" + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" }, - "cache": { - "type": "Microsoft.ApiManagement/service/caches", - "apiVersion": "2022-08-01", - "name": "[format('{0}/{1}', parameters('apiManagementServiceName'), parameters('name'))]", + "A": { + "type": "Microsoft.Network/privateDnsZones/A", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", "properties": { - "description": "[parameters('description')]", - "connectionString": "[parameters('connectionString')]", - "useFromLocation": "[parameters('useFromLocation')]", - "resourceId": "[parameters('resourceId')]" + "aRecords": "[parameters('aRecords')]", + "metadata": "[parameters('metadata')]", + "ttl": "[parameters('ttl')]" } + }, + "A_roleAssignments": { + "copy": { + "name": "A_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/A/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/A', parameters('privateDnsZoneName'), 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": [ + "A" + ] } }, "outputs": { - "resourceId": { + "name": { "type": "string", "metadata": { - "description": "The resource ID of the API management service cache." + "description": "The name of the deployed A record." }, - "value": "[resourceId('Microsoft.ApiManagement/service/caches', parameters('apiManagementServiceName'), parameters('name'))]" + "value": "[parameters('name')]" }, - "name": { + "resourceId": { "type": "string", "metadata": { - "description": "The name of the API management service cache." + "description": "The resource ID of the deployed A record." }, - "value": "[parameters('name')]" + "value": "[resourceId('Microsoft.Network/privateDnsZones/A', parameters('privateDnsZoneName'), parameters('name'))]" }, "resourceGroupName": { "type": "string", "metadata": { - "description": "The resource group the API management service cache was deployed into." + "description": "The resource group of the deployed A record." }, "value": "[resourceGroup().name]" } @@ -64468,225 +76569,249 @@ } }, "dependsOn": [ - "service" + "privateDnsZone" ] }, - "service_apiDiagnostics": { + "privateDnsZone_AAAA": { "copy": { - "name": "service_apiDiagnostics", - "count": "[length(parameters('apiDiagnostics'))]" + "name": "privateDnsZone_AAAA", + "count": "[length(coalesce(parameters('aaaa'), createArray()))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-Apim-Api-Diagnostic-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "name": "[format('{0}-PrivateDnsZone-AAAARecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", - "parameters": { - "apiManagementServiceName": { - "value": "[parameters('name')]" - }, - "apiName": { - "value": "[parameters('apiDiagnostics')[copyIndex()].apiName]" - }, - "loggerName": { - "value": "[tryGet(parameters('apiDiagnostics')[copyIndex()], 'loggerName')]" + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" }, "name": { - "value": "[tryGet(parameters('apiDiagnostics')[copyIndex()], 'name')]" - }, - "alwaysLog": { - "value": "[tryGet(parameters('apiDiagnostics')[copyIndex()], 'alwaysLog')]" - }, - "backend": { - "value": "[tryGet(parameters('apiDiagnostics')[copyIndex()], 'backend')]" - }, - "frontend": { - "value": "[tryGet(parameters('apiDiagnostics')[copyIndex()], 'frontend')]" - }, - "httpCorrelationProtocol": { - "value": "[tryGet(parameters('apiDiagnostics')[copyIndex()], 'httpCorrelationProtocol')]" - }, - "logClientIp": { - "value": "[tryGet(parameters('apiDiagnostics')[copyIndex()], 'logClientIp')]" + "value": "[coalesce(parameters('aaaa'), createArray())[copyIndex()].name]" }, - "metrics": { - "value": "[tryGet(parameters('apiDiagnostics')[copyIndex()], 'metrics')]" + "aaaaRecords": { + "value": "[tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'aaaaRecords')]" }, - "operationNameFormat": { - "value": "[tryGet(parameters('apiDiagnostics')[copyIndex()], 'operationNameFormat')]" + "metadata": { + "value": "[tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'metadata')]" }, - "samplingPercentage": { - "value": "[tryGet(parameters('apiDiagnostics')[copyIndex()], 'samplingPercentage')]" + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'ttl'), 3600)]" }, - "verbosity": { - "value": "[tryGet(parameters('apiDiagnostics')[copyIndex()], 'verbosity')]" + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'roleAssignments')]" } }, "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.34.1.11899", - "templateHash": "220134378763209880" + "version": "0.32.4.45862", + "templateHash": "16709340450244912125" }, - "name": "API Management Service APIs Diagnostics.", - "description": "This module deploys an API Management Service API Diagnostics." + "name": "Private DNS Zone AAAA record", + "description": "This module deploys a Private DNS Zone AAAA record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "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." + } + } + } + }, + "nullable": true + } }, "parameters": { - "apiManagementServiceName": { - "type": "string", - "metadata": { - "description": "Required. The name of the parent API Management service." - } - }, - "apiName": { - "type": "string", - "metadata": { - "description": "Required. The name of the parent API." - } - }, - "loggerName": { + "privateDnsZoneName": { "type": "string", "metadata": { - "description": "Required. The name of the logger." + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." } }, "name": { "type": "string", - "defaultValue": "local", - "allowedValues": [ - "azuremonitor", - "applicationinsights", - "local" - ], - "metadata": { - "description": "Optional. Type of diagnostic resource." - } - }, - "alwaysLog": { - "type": "string", - "defaultValue": "allErrors", "metadata": { - "description": "Optional. Specifies for what type of messages sampling settings should not apply." + "description": "Required. The name of the AAAA record." } }, - "backend": { - "type": "object", - "defaultValue": {}, + "aaaaRecords": { + "type": "array", + "nullable": true, "metadata": { - "description": "Optional. Diagnostic settings for incoming/outgoing HTTP messages to the Backend." + "description": "Optional. The list of AAAA records in the record set." } }, - "frontend": { + "metadata": { "type": "object", - "defaultValue": {}, - "metadata": { - "description": "Optional. Diagnostic settings for incoming/outgoing HTTP messages to the Gateway." - } - }, - "httpCorrelationProtocol": { - "type": "string", - "defaultValue": "Legacy", - "allowedValues": [ - "Legacy", - "None", - "W3C" - ], - "metadata": { - "description": "Conditional. Sets correlation protocol to use for Application Insights diagnostics. Required if using Application Insights." - } - }, - "logClientIp": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "Optional. Log the ClientIP." - } - }, - "metrics": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "Conditional. Emit custom metrics via emit-metric policy. Required if using Application Insights." - } - }, - "operationNameFormat": { - "type": "string", - "defaultValue": "Name", - "allowedValues": [ - "Name", - "URI" - ], + "nullable": true, "metadata": { - "description": "Conditional. The format of the Operation Name for Application Insights telemetries. Required if using Application Insights." + "description": "Optional. The metadata attached to the record set." } }, - "samplingPercentage": { + "ttl": { "type": "int", - "defaultValue": 100, + "defaultValue": 3600, "metadata": { - "description": "Optional. Rate of sampling for fixed-rate sampling. Specifies the percentage of requests that are logged. 0% sampling means zero requests logged, while 100% sampling means all requests logged." + "description": "Optional. The TTL (time-to-live) of the records in the record set." } }, - "verbosity": { - "type": "string", - "defaultValue": "error", - "allowedValues": [ - "error", - "information", - "verbose" - ], + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", "metadata": { - "description": "Optional. The verbosity level applied to traces emitted by trace policies." + "description": "Optional. Array of role assignments to create." } } }, - "resources": [ - { - "type": "Microsoft.ApiManagement/service/apis/diagnostics", - "apiVersion": "2022-08-01", - "name": "[format('{0}/{1}/{2}', parameters('apiManagementServiceName'), parameters('apiName'), parameters('name'))]", + "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')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "AAAA": { + "type": "Microsoft.Network/privateDnsZones/AAAA", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", "properties": { - "alwaysLog": "[parameters('alwaysLog')]", - "backend": "[parameters('backend')]", - "frontend": "[parameters('frontend')]", - "httpCorrelationProtocol": "[parameters('httpCorrelationProtocol')]", - "logClientIp": "[parameters('logClientIp')]", - "loggerId": "[resourceId('Microsoft.ApiManagement/service/loggers', parameters('apiManagementServiceName'), parameters('loggerName'))]", - "metrics": "[parameters('metrics')]", - "operationNameFormat": "[parameters('operationNameFormat')]", - "sampling": { - "percentage": "[parameters('samplingPercentage')]", - "samplingType": "fixed" - }, - "verbosity": "[parameters('verbosity')]" + "aaaaRecords": "[parameters('aaaaRecords')]", + "metadata": "[parameters('metadata')]", + "ttl": "[parameters('ttl')]" } + }, + "AAAA_roleAssignments": { + "copy": { + "name": "AAAA_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/AAAA/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/AAAA', parameters('privateDnsZoneName'), 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": [ + "AAAA" + ] } - ], + }, "outputs": { - "resourceId": { + "name": { "type": "string", "metadata": { - "description": "The resource ID of the API diagnostic." + "description": "The name of the deployed AAAA record." }, - "value": "[resourceId('Microsoft.ApiManagement/service/apis/diagnostics', parameters('apiManagementServiceName'), parameters('apiName'), parameters('name'))]" + "value": "[parameters('name')]" }, - "name": { + "resourceId": { "type": "string", "metadata": { - "description": "The name of the API diagnostic." + "description": "The resource ID of the deployed AAAA record." }, - "value": "[parameters('name')]" + "value": "[resourceId('Microsoft.Network/privateDnsZones/AAAA', parameters('privateDnsZoneName'), parameters('name'))]" }, "resourceGroupName": { "type": "string", "metadata": { - "description": "The resource group the API diagnostic was deployed into." + "description": "The resource group of the deployed AAAA record." }, "value": "[resourceGroup().name]" } @@ -64694,63 +76819,40 @@ } }, "dependsOn": [ - "service", - "service_apis", - "service_loggers" + "privateDnsZone" ] }, - "service_identityProviders": { + "privateDnsZone_CNAME": { "copy": { - "name": "service_identityProviders", - "count": "[length(parameters('identityProviders'))]" + "name": "privateDnsZone_CNAME", + "count": "[length(coalesce(parameters('cname'), createArray()))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-Apim-IdentityProvider-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "name": "[format('{0}-PrivateDnsZone-CNAMERecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "apiManagementServiceName": { + "privateDnsZoneName": { "value": "[parameters('name')]" }, "name": { - "value": "[parameters('identityProviders')[copyIndex()].name]" - }, - "allowedTenants": { - "value": "[coalesce(tryGet(parameters('identityProviders')[copyIndex()], 'allowedTenants'), createArray())]" - }, - "authority": { - "value": "[coalesce(tryGet(parameters('identityProviders')[copyIndex()], 'authority'), '')]" - }, - "clientId": { - "value": "[coalesce(tryGet(parameters('identityProviders')[copyIndex()], 'clientId'), '')]" - }, - "clientLibrary": { - "value": "[coalesce(tryGet(parameters('identityProviders')[copyIndex()], 'clientLibrary'), '')]" - }, - "clientSecret": { - "value": "[coalesce(tryGet(parameters('identityProviders')[copyIndex()], 'clientSecret'), '')]" - }, - "passwordResetPolicyName": { - "value": "[coalesce(tryGet(parameters('identityProviders')[copyIndex()], 'passwordResetPolicyName'), '')]" - }, - "profileEditingPolicyName": { - "value": "[coalesce(tryGet(parameters('identityProviders')[copyIndex()], 'profileEditingPolicyName'), '')]" + "value": "[coalesce(parameters('cname'), createArray())[copyIndex()].name]" }, - "signInPolicyName": { - "value": "[coalesce(tryGet(parameters('identityProviders')[copyIndex()], 'signInPolicyName'), '')]" + "cnameRecord": { + "value": "[tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'cnameRecord')]" }, - "signInTenant": { - "value": "[coalesce(tryGet(parameters('identityProviders')[copyIndex()], 'signInTenant'), '')]" + "metadata": { + "value": "[tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'metadata')]" }, - "signUpPolicyName": { - "value": "[coalesce(tryGet(parameters('identityProviders')[copyIndex()], 'signUpPolicyName'), '')]" + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'ttl'), 3600)]" }, - "type": { - "value": "[coalesce(tryGet(parameters('identityProviders')[copyIndex()], 'type'), 'aad')]" + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'roleAssignments')]" } }, "template": { @@ -64760,163 +76862,206 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.34.1.11899", - "templateHash": "1181738654147388268" + "version": "0.32.4.45862", + "templateHash": "9976020649752073181" }, - "name": "API Management Service Identity Providers", - "description": "This module deploys an API Management Service Identity Provider." + "name": "Private DNS Zone CNAME record", + "description": "This module deploys a Private DNS Zone CNAME record.", + "owner": "Azure/module-maintainers" }, - "parameters": { - "apiManagementServiceName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent API Management service. Required if the template is used in a standalone deployment." - } - }, - "allowedTenants": { + "definitions": { + "roleAssignmentType": { "type": "array", - "defaultValue": [], - "metadata": { - "description": "Optional. List of Allowed Tenants when configuring Azure Active Directory login. - string." - } - }, - "authority": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Optional. OpenID Connect discovery endpoint hostname for AAD or AAD B2C." - } - }, - "clientId": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Conditional. Client ID of the Application in the external Identity Provider. Required if identity provider is used." - } - }, - "clientLibrary": { - "type": "string", - "nullable": true, - "allowedValues": [ - "ADAL", - "MSAL-2" - ], - "metadata": { - "description": "Optional. The client library to be used in the developer portal. Only applies to AAD and AAD B2C Identity Provider." - } - }, - "clientSecret": { - "type": "securestring", - "defaultValue": "", - "metadata": { - "description": "Conditional. Client secret of the Application in external Identity Provider, used to authenticate login request. Required if identity provider is used." - } - }, - "passwordResetPolicyName": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Optional. Password Reset Policy Name. Only applies to AAD B2C Identity Provider." - } - }, - "profileEditingPolicyName": { + "items": { + "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." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { "type": "string", - "defaultValue": "", "metadata": { - "description": "Optional. Profile Editing Policy Name. Only applies to AAD B2C Identity Provider." + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." } }, - "signInPolicyName": { + "name": { "type": "string", - "defaultValue": "", "metadata": { - "description": "Optional. Signin Policy Name. Only applies to AAD B2C Identity Provider." + "description": "Required. The name of the CNAME record." } }, - "signInTenant": { - "type": "string", - "defaultValue": "", + "cnameRecord": { + "type": "object", + "nullable": true, "metadata": { - "description": "Optional. The TenantId to use instead of Common when logging into Active Directory." + "description": "Optional. A CNAME record." } }, - "signUpPolicyName": { - "type": "string", - "defaultValue": "", + "metadata": { + "type": "object", + "nullable": true, "metadata": { - "description": "Optional. Signup Policy Name. Only applies to AAD B2C Identity Provider." + "description": "Optional. The metadata attached to the record set." } }, - "type": { - "type": "string", - "defaultValue": "aad", - "allowedValues": [ - "aad", - "aadB2C", - "facebook", - "google", - "microsoft", - "twitter" - ], + "ttl": { + "type": "int", + "defaultValue": 3600, "metadata": { - "description": "Optional. Identity Provider Type identifier." + "description": "Optional. The TTL (time-to-live) of the records in the record set." } }, - "name": { - "type": "string", + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", "metadata": { - "description": "Required. Identity provider name." + "description": "Optional. Array of role assignments to create." } } }, "variables": { - "isAadB2C": "[equals(parameters('type'), 'aadB2C')]" + "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')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } }, "resources": { - "service": { + "privateDnsZone": { "existing": true, - "type": "Microsoft.ApiManagement/service", - "apiVersion": "2023-05-01-preview", - "name": "[parameters('apiManagementServiceName')]" + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" }, - "identityProvider": { - "type": "Microsoft.ApiManagement/service/identityProviders", - "apiVersion": "2022-08-01", - "name": "[format('{0}/{1}', parameters('apiManagementServiceName'), parameters('name'))]", + "CNAME": { + "type": "Microsoft.Network/privateDnsZones/CNAME", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", "properties": { - "type": "[parameters('type')]", - "signinTenant": "[parameters('signInTenant')]", - "allowedTenants": "[parameters('allowedTenants')]", - "authority": "[parameters('authority')]", - "signupPolicyName": "[if(variables('isAadB2C'), parameters('signUpPolicyName'), null())]", - "signinPolicyName": "[if(variables('isAadB2C'), parameters('signInPolicyName'), null())]", - "profileEditingPolicyName": "[if(variables('isAadB2C'), parameters('profileEditingPolicyName'), null())]", - "passwordResetPolicyName": "[if(variables('isAadB2C'), parameters('passwordResetPolicyName'), null())]", - "clientId": "[parameters('clientId')]", - "clientLibrary": "[parameters('clientLibrary')]", - "clientSecret": "[parameters('clientSecret')]" + "cnameRecord": "[parameters('cnameRecord')]", + "metadata": "[parameters('metadata')]", + "ttl": "[parameters('ttl')]" } + }, + "CNAME_roleAssignments": { + "copy": { + "name": "CNAME_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/CNAME/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/CNAME', parameters('privateDnsZoneName'), 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": [ + "CNAME" + ] } }, "outputs": { - "resourceId": { + "name": { "type": "string", "metadata": { - "description": "The resource ID of the API management service identity provider." + "description": "The name of the deployed CNAME record." }, - "value": "[resourceId('Microsoft.ApiManagement/service/identityProviders', parameters('apiManagementServiceName'), parameters('name'))]" + "value": "[parameters('name')]" }, - "name": { + "resourceId": { "type": "string", "metadata": { - "description": "The name of the API management service identity provider." + "description": "The resource ID of the deployed CNAME record." }, - "value": "[parameters('name')]" + "value": "[resourceId('Microsoft.Network/privateDnsZones/CNAME', parameters('privateDnsZoneName'), parameters('name'))]" }, "resourceGroupName": { "type": "string", "metadata": { - "description": "The resource group the API management service identity provider was deployed into." + "description": "The resource group of the deployed CNAME record." }, "value": "[resourceGroup().name]" } @@ -64924,141 +77069,249 @@ } }, "dependsOn": [ - "service" + "privateDnsZone" ] }, - "service_loggers": { + "privateDnsZone_MX": { "copy": { - "name": "service_loggers", - "count": "[length(parameters('loggers'))]" + "name": "privateDnsZone_MX", + "count": "[length(coalesce(parameters('mx'), createArray()))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-Apim-Logger-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "name": "[format('{0}-PrivateDnsZone-MXRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "name": { - "value": "[parameters('loggers')[copyIndex()].name]" - }, - "apiManagementServiceName": { + "privateDnsZoneName": { "value": "[parameters('name')]" }, - "credentials": { - "value": "[coalesce(tryGet(parameters('loggers')[copyIndex()], 'credentials'), createObject())]" + "name": { + "value": "[coalesce(parameters('mx'), createArray())[copyIndex()].name]" }, - "isBuffered": { - "value": "[tryGet(parameters('loggers')[copyIndex()], 'isBuffered')]" + "metadata": { + "value": "[tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'metadata')]" }, - "description": { - "value": "[tryGet(parameters('loggers')[copyIndex()], 'loggerDescription')]" + "mxRecords": { + "value": "[tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'mxRecords')]" }, - "type": { - "value": "[coalesce(tryGet(parameters('loggers')[copyIndex()], 'loggerType'), 'azureMonitor')]" + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'ttl'), 3600)]" }, - "targetResourceId": { - "value": "[coalesce(tryGet(parameters('loggers')[copyIndex()], 'targetResourceId'), '')]" + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'roleAssignments')]" } }, "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.34.1.11899", - "templateHash": "13484364421614720756" + "version": "0.32.4.45862", + "templateHash": "2520323624213076361" }, - "name": "API Management Service Loggers", - "description": "This module deploys an API Management Service Logger." + "name": "Private DNS Zone MX record", + "description": "This module deploys a Private DNS Zone MX record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "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." + } + } + } + }, + "nullable": true + } }, "parameters": { - "apiManagementServiceName": { + "privateDnsZoneName": { "type": "string", "metadata": { - "description": "Conditional. The name of the parent API Management service. Required if the template is used in a standalone deployment." + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." } }, "name": { "type": "string", "metadata": { - "description": "Required. Resource Name." + "description": "Required. The name of the MX record." } }, - "description": { - "type": "string", - "defaultValue": "", + "metadata": { + "type": "object", + "nullable": true, "metadata": { - "description": "Optional. Logger description." + "description": "Optional. The metadata attached to the record set." } }, - "isBuffered": { - "type": "bool", - "defaultValue": true, + "mxRecords": { + "type": "array", + "nullable": true, "metadata": { - "description": "Optional. Whether records are buffered in the logger before publishing." + "description": "Optional. The list of MX records in the record set." } }, - "type": { - "type": "string", - "allowedValues": [ - "applicationInsights", - "azureEventHub", - "azureMonitor" - ], + "ttl": { + "type": "int", + "defaultValue": 3600, "metadata": { - "description": "Required. Logger type." + "description": "Optional. The TTL (time-to-live) of the records in the record set." } }, - "targetResourceId": { - "type": "string", + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", "metadata": { - "description": "Conditional. Required if loggerType = applicationInsights or azureEventHub. Azure Resource Id of a log target (either Azure Event Hub resource or Azure Application Insights resource)." + "description": "Optional. Array of role assignments to create." } - }, - "credentials": { - "type": "secureObject", - "metadata": { - "description": "Conditional. Required if loggerType = applicationInsights or azureEventHub. The name and SendRule connection string of the event hub for azureEventHub logger. Instrumentation key for applicationInsights logger." + } + }, + "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')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, - "resources": [ - { - "type": "Microsoft.ApiManagement/service/loggers", - "apiVersion": "2022-08-01", - "name": "[format('{0}/{1}', parameters('apiManagementServiceName'), parameters('name'))]", + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "MX": { + "type": "Microsoft.Network/privateDnsZones/MX", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", "properties": { - "credentials": "[parameters('credentials')]", - "description": "[parameters('description')]", - "isBuffered": "[parameters('isBuffered')]", - "loggerType": "[parameters('type')]", - "resourceId": "[parameters('targetResourceId')]" + "metadata": "[parameters('metadata')]", + "mxRecords": "[parameters('mxRecords')]", + "ttl": "[parameters('ttl')]" } + }, + "MX_roleAssignments": { + "copy": { + "name": "MX_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/MX/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/MX', parameters('privateDnsZoneName'), 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": [ + "MX" + ] } - ], + }, "outputs": { - "resourceId": { + "name": { "type": "string", "metadata": { - "description": "The resource ID of the logger." + "description": "The name of the deployed MX record." }, - "value": "[resourceId('Microsoft.ApiManagement/service/loggers', parameters('apiManagementServiceName'), parameters('name'))]" + "value": "[parameters('name')]" }, - "name": { + "resourceId": { "type": "string", "metadata": { - "description": "The name of the logger." + "description": "The resource ID of the deployed MX record." }, - "value": "[parameters('name')]" + "value": "[resourceId('Microsoft.Network/privateDnsZones/MX', parameters('privateDnsZoneName'), parameters('name'))]" }, "resourceGroupName": { "type": "string", "metadata": { - "description": "The resource group the named value was deployed into." + "description": "The resource group of the deployed MX record." }, "value": "[resourceGroup().name]" } @@ -65066,44 +77319,40 @@ } }, "dependsOn": [ - "service", - "service_namedValues" + "privateDnsZone" ] }, - "service_namedValues": { + "privateDnsZone_PTR": { "copy": { - "name": "service_namedValues", - "count": "[length(parameters('namedValues'))]" + "name": "privateDnsZone_PTR", + "count": "[length(coalesce(parameters('ptr'), createArray()))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-Apim-NamedValue-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "name": "[format('{0}-PrivateDnsZone-PTRRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "apiManagementServiceName": { + "privateDnsZoneName": { "value": "[parameters('name')]" }, - "displayName": { - "value": "[parameters('namedValues')[copyIndex()].displayName]" - }, - "keyVault": { - "value": "[coalesce(tryGet(parameters('namedValues')[copyIndex()], 'keyVault'), createObject())]" - }, "name": { - "value": "[parameters('namedValues')[copyIndex()].name]" + "value": "[coalesce(parameters('ptr'), createArray())[copyIndex()].name]" }, - "tags": { - "value": "[tryGet(parameters('namedValues')[copyIndex()], 'tags')]" + "metadata": { + "value": "[tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'metadata')]" }, - "secret": { - "value": "[coalesce(tryGet(parameters('namedValues')[copyIndex()], 'secret'), false())]" + "ptrRecords": { + "value": "[tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'ptrRecords')]" }, - "value": { - "value": "[coalesce(tryGet(parameters('namedValues')[copyIndex()], 'value'), parameters('newGuidValue'))]" + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'ttl'), 3600)]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'roleAssignments')]" } }, "template": { @@ -65113,102 +77362,206 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.34.1.11899", - "templateHash": "17330477206748787798" + "version": "0.32.4.45862", + "templateHash": "3080404733048745471" }, - "name": "API Management Service Named Values", - "description": "This module deploys an API Management Service Named Value." + "name": "Private DNS Zone PTR record", + "description": "This module deploys a Private DNS Zone PTR record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "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." + } + } + } + }, + "nullable": true + } }, "parameters": { - "apiManagementServiceName": { + "privateDnsZoneName": { "type": "string", "metadata": { - "description": "Conditional. The name of the parent API Management service. Required if the template is used in a standalone deployment." + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." } }, - "displayName": { + "name": { "type": "string", "metadata": { - "description": "Required. Unique name of NamedValue. It may contain only letters, digits, period, dash, and underscore characters." + "description": "Required. The name of the PTR record." } }, - "keyVault": { + "metadata": { "type": "object", - "defaultValue": {}, - "metadata": { - "description": "Optional. KeyVault location details of the namedValue." - } - }, - "name": { - "type": "string", + "nullable": true, "metadata": { - "description": "Required. Named value Name." + "description": "Optional. The metadata attached to the record set." } }, - "tags": { + "ptrRecords": { "type": "array", "nullable": true, "metadata": { - "description": "Optional. Tags that when provided can be used to filter the NamedValue list. - string." + "description": "Optional. The list of PTR records in the record set." } }, - "secret": { - "type": "bool", - "defaultValue": false, + "ttl": { + "type": "int", + "defaultValue": 3600, "metadata": { - "description": "Optional. Determines whether the value is a secret and should be encrypted or not. Default value is false." + "description": "Optional. The TTL (time-to-live) of the records in the record set." } }, - "value": { - "type": "string", - "defaultValue": "[newGuid()]", + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", "metadata": { - "description": "Optional. Value of the NamedValue. Can contain policy expressions. It may not be empty or consist only of whitespace. This property will not be filled on 'GET' operations! Use '/listSecrets' POST request to get the value." + "description": "Optional. Array of role assignments to create." } } }, "variables": { - "keyVaultEmpty": "[empty(parameters('keyVault'))]" + "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')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } }, "resources": { - "service": { + "privateDnsZone": { "existing": true, - "type": "Microsoft.ApiManagement/service", - "apiVersion": "2023-05-01-preview", - "name": "[parameters('apiManagementServiceName')]" + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" }, - "namedValue": { - "type": "Microsoft.ApiManagement/service/namedValues", - "apiVersion": "2022-08-01", - "name": "[format('{0}/{1}', parameters('apiManagementServiceName'), parameters('name'))]", + "PTR": { + "type": "Microsoft.Network/privateDnsZones/PTR", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", "properties": { - "tags": "[parameters('tags')]", - "secret": "[parameters('secret')]", - "displayName": "[parameters('displayName')]", - "value": "[if(variables('keyVaultEmpty'), parameters('value'), null())]", - "keyVault": "[if(not(variables('keyVaultEmpty')), parameters('keyVault'), null())]" + "metadata": "[parameters('metadata')]", + "ptrRecords": "[parameters('ptrRecords')]", + "ttl": "[parameters('ttl')]" } + }, + "PTR_roleAssignments": { + "copy": { + "name": "PTR_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/PTR/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/PTR', parameters('privateDnsZoneName'), 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": [ + "PTR" + ] } }, "outputs": { - "resourceId": { + "name": { "type": "string", "metadata": { - "description": "The resource ID of the named value." + "description": "The name of the deployed PTR record." }, - "value": "[resourceId('Microsoft.ApiManagement/service/namedValues', parameters('apiManagementServiceName'), parameters('name'))]" + "value": "[parameters('name')]" }, - "name": { + "resourceId": { "type": "string", "metadata": { - "description": "The name of the named value." + "description": "The resource ID of the deployed PTR record." }, - "value": "[parameters('name')]" + "value": "[resourceId('Microsoft.Network/privateDnsZones/PTR', parameters('privateDnsZoneName'), parameters('name'))]" }, "resourceGroupName": { "type": "string", "metadata": { - "description": "The resource group the named value was deployed into." + "description": "The resource group of the deployed PTR record." }, "value": "[resourceGroup().name]" } @@ -65216,98 +77569,249 @@ } }, "dependsOn": [ - "service" + "privateDnsZone" ] }, - "service_portalsettings": { + "privateDnsZone_SOA": { "copy": { - "name": "service_portalsettings", - "count": "[length(parameters('portalsettings'))]" + "name": "privateDnsZone_SOA", + "count": "[length(coalesce(parameters('soa'), createArray()))]" }, - "condition": "[not(empty(parameters('portalsettings')[copyIndex()].properties))]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-Apim-PortalSetting-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "name": "[format('{0}-PrivateDnsZone-SOARecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "apiManagementServiceName": { + "privateDnsZoneName": { "value": "[parameters('name')]" }, "name": { - "value": "[parameters('portalsettings')[copyIndex()].name]" + "value": "[coalesce(parameters('soa'), createArray())[copyIndex()].name]" }, - "properties": { - "value": "[parameters('portalsettings')[copyIndex()].properties]" + "metadata": { + "value": "[tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'metadata')]" + }, + "soaRecord": { + "value": "[tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'soaRecord')]" + }, + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'ttl'), 3600)]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'roleAssignments')]" } }, "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.34.1.11899", - "templateHash": "11836027592515216348" + "version": "0.32.4.45862", + "templateHash": "6653951445614700931" }, - "name": "API Management Service Portal Settings", - "description": "This module deploys an API Management Service Portal Setting." + "name": "Private DNS Zone SOA record", + "description": "This module deploys a Private DNS Zone SOA record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "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." + } + } + } + }, + "nullable": true + } }, "parameters": { - "apiManagementServiceName": { + "privateDnsZoneName": { "type": "string", "metadata": { - "description": "Conditional. The name of the parent API Management service. Required if the template is used in a standalone deployment." + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." } }, "name": { "type": "string", - "allowedValues": [ - "delegation", - "signin", - "signup" - ], "metadata": { - "description": "Required. Portal setting name." + "description": "Required. The name of the SOA record." } }, - "properties": { + "metadata": { "type": "object", + "nullable": true, "metadata": { - "description": "Required. Portal setting properties." + "description": "Optional. The metadata attached to the record set." + } + }, + "soaRecord": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. A SOA record." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." } } }, - "resources": [ - { - "type": "Microsoft.ApiManagement/service/portalsettings", - "apiVersion": "2022-08-01", - "name": "[format('{0}/{1}', parameters('apiManagementServiceName'), parameters('name'))]", - "properties": "[parameters('properties')]" + "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')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } - ], + }, + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "SOA": { + "type": "Microsoft.Network/privateDnsZones/SOA", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "properties": { + "metadata": "[parameters('metadata')]", + "soaRecord": "[parameters('soaRecord')]", + "ttl": "[parameters('ttl')]" + } + }, + "SOA_roleAssignments": { + "copy": { + "name": "SOA_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/SOA/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/SOA', parameters('privateDnsZoneName'), 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": [ + "SOA" + ] + } + }, "outputs": { - "resourceId": { + "name": { "type": "string", "metadata": { - "description": "The resource ID of the API management service portal setting." + "description": "The name of the deployed SOA record." }, - "value": "[resourceId('Microsoft.ApiManagement/service/portalsettings', parameters('apiManagementServiceName'), parameters('name'))]" + "value": "[parameters('name')]" }, - "name": { + "resourceId": { "type": "string", "metadata": { - "description": "The name of the API management service portal setting." + "description": "The resource ID of the deployed SOA record." }, - "value": "[parameters('name')]" + "value": "[resourceId('Microsoft.Network/privateDnsZones/SOA', parameters('privateDnsZoneName'), parameters('name'))]" }, "resourceGroupName": { "type": "string", "metadata": { - "description": "The resource group the API management service portal setting was deployed into." + "description": "The resource group of the deployed SOA record." }, "value": "[resourceGroup().name]" } @@ -65315,109 +77819,249 @@ } }, "dependsOn": [ - "service" + "privateDnsZone" ] }, - "service_policies": { + "privateDnsZone_SRV": { "copy": { - "name": "service_policies", - "count": "[length(parameters('policies'))]" + "name": "privateDnsZone_SRV", + "count": "[length(coalesce(parameters('srv'), createArray()))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-Apim-Policy-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "name": "[format('{0}-PrivateDnsZone-SRVRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "apiManagementServiceName": { + "privateDnsZoneName": { "value": "[parameters('name')]" }, - "value": { - "value": "[parameters('policies')[copyIndex()].value]" + "name": { + "value": "[coalesce(parameters('srv'), createArray())[copyIndex()].name]" }, - "format": { - "value": "[coalesce(tryGet(parameters('policies')[copyIndex()], 'format'), 'xml')]" + "metadata": { + "value": "[tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'metadata')]" + }, + "srvRecords": { + "value": "[tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'srvRecords')]" + }, + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'ttl'), 3600)]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'roleAssignments')]" } }, "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.34.1.11899", - "templateHash": "11662265993484377181" + "version": "0.32.4.45862", + "templateHash": "5790774778713328446" }, - "name": "API Management Service Policies", - "description": "This module deploys an API Management Service Policy." + "name": "Private DNS Zone SRV record", + "description": "This module deploys a Private DNS Zone SRV record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "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." + } + } + } + }, + "nullable": true + } }, "parameters": { - "apiManagementServiceName": { + "privateDnsZoneName": { "type": "string", "metadata": { - "description": "Conditional. The name of the parent API Management service. Required if the template is used in a standalone deployment." + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." } }, "name": { "type": "string", - "defaultValue": "policy", "metadata": { - "description": "Optional. The name of the policy." + "description": "Required. The name of the SRV record." } }, - "format": { - "type": "string", - "defaultValue": "xml", - "allowedValues": [ - "rawxml", - "rawxml-link", - "xml", - "xml-link" - ], + "metadata": { + "type": "object", + "nullable": true, "metadata": { - "description": "Optional. Format of the policyContent." + "description": "Optional. The metadata attached to the record set." } }, - "value": { - "type": "string", + "srvRecords": { + "type": "array", + "nullable": true, "metadata": { - "description": "Required. Contents of the Policy as defined by the format." + "description": "Optional. The list of SRV records in the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." } } }, - "resources": [ - { - "type": "Microsoft.ApiManagement/service/policies", - "apiVersion": "2022-08-01", - "name": "[format('{0}/{1}', parameters('apiManagementServiceName'), parameters('name'))]", + "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')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "SRV": { + "type": "Microsoft.Network/privateDnsZones/SRV", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "properties": { + "metadata": "[parameters('metadata')]", + "srvRecords": "[parameters('srvRecords')]", + "ttl": "[parameters('ttl')]" + } + }, + "SRV_roleAssignments": { + "copy": { + "name": "SRV_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/SRV/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/SRV', parameters('privateDnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "format": "[parameters('format')]", - "value": "[parameters('value')]" - } + "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": [ + "SRV" + ] } - ], + }, "outputs": { - "resourceId": { + "name": { "type": "string", "metadata": { - "description": "The resource ID of the API management service policy." + "description": "The name of the deployed SRV record." }, - "value": "[resourceId('Microsoft.ApiManagement/service/policies', parameters('apiManagementServiceName'), parameters('name'))]" + "value": "[parameters('name')]" }, - "name": { + "resourceId": { "type": "string", "metadata": { - "description": "The name of the API management service policy." + "description": "The resource ID of the deployed SRV record." }, - "value": "[parameters('name')]" + "value": "[resourceId('Microsoft.Network/privateDnsZones/SRV', parameters('privateDnsZoneName'), parameters('name'))]" }, "resourceGroupName": { "type": "string", "metadata": { - "description": "The resource group the API management service policy was deployed into." + "description": "The resource group of the deployed SRV record." }, "value": "[resourceGroup().name]" } @@ -65425,430 +78069,293 @@ } }, "dependsOn": [ - "service" + "privateDnsZone" ] }, - "service_products": { + "privateDnsZone_TXT": { "copy": { - "name": "service_products", - "count": "[length(parameters('products'))]" + "name": "privateDnsZone_TXT", + "count": "[length(coalesce(parameters('txt'), createArray()))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-Apim-Product-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "name": "[format('{0}-PrivateDnsZone-TXTRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "displayName": { - "value": "[parameters('products')[copyIndex()].displayName]" - }, - "apiManagementServiceName": { + "privateDnsZoneName": { "value": "[parameters('name')]" }, - "apis": { - "value": "[coalesce(tryGet(parameters('products')[copyIndex()], 'apis'), createArray())]" - }, - "approvalRequired": { - "value": "[coalesce(tryGet(parameters('products')[copyIndex()], 'approvalRequired'), false())]" - }, - "groups": { - "value": "[coalesce(tryGet(parameters('products')[copyIndex()], 'groups'), createArray())]" - }, "name": { - "value": "[parameters('products')[copyIndex()].name]" - }, - "description": { - "value": "[coalesce(tryGet(parameters('products')[copyIndex()], 'description'), '')]" + "value": "[coalesce(parameters('txt'), createArray())[copyIndex()].name]" }, - "state": { - "value": "[coalesce(tryGet(parameters('products')[copyIndex()], 'state'), 'published')]" + "metadata": { + "value": "[tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'metadata')]" }, - "subscriptionRequired": { - "value": "[coalesce(tryGet(parameters('products')[copyIndex()], 'subscriptionRequired'), false())]" + "txtRecords": { + "value": "[tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'txtRecords')]" }, - "subscriptionsLimit": { - "value": "[coalesce(tryGet(parameters('products')[copyIndex()], 'subscriptionsLimit'), 1)]" + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'ttl'), 3600)]" }, - "terms": { - "value": "[coalesce(tryGet(parameters('products')[copyIndex()], 'terms'), '')]" + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'roleAssignments')]" } }, "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.34.1.11899", - "templateHash": "8113178935422733202" + "version": "0.32.4.45862", + "templateHash": "1855369119498044639" }, - "name": "API Management Service Products", - "description": "This module deploys an API Management Service Product." + "name": "Private DNS Zone TXT record", + "description": "This module deploys a Private DNS Zone TXT record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "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." + } + } + } + }, + "nullable": true + } }, "parameters": { - "apiManagementServiceName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent API Management service. Required if the template is used in a standalone deployment." - } - }, - "displayName": { - "type": "string", - "maxLength": 300, - "metadata": { - "description": "Required. API Management Service Products name. Must be 1 to 300 characters long." - } - }, - "approvalRequired": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "Optional. Whether subscription approval is required. If false, new subscriptions will be approved automatically enabling developers to call the products APIs immediately after subscribing. If true, administrators must manually approve the subscription before the developer can any of the products APIs. Can be present only if subscriptionRequired property is present and has a value of false." - } - }, - "description": { + "privateDnsZoneName": { "type": "string", - "defaultValue": "", - "metadata": { - "description": "Optional. Product description. May include HTML formatting tags." - } - }, - "apis": { - "type": "array", - "defaultValue": [], - "metadata": { - "description": "Optional. Array of Product APIs." - } - }, - "groups": { - "type": "array", - "defaultValue": [], "metadata": { - "description": "Optional. Array of Product Groups." + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." } }, "name": { "type": "string", "metadata": { - "description": "Required. Product Name." + "description": "Required. The name of the TXT record." } }, - "state": { - "type": "string", - "defaultValue": "published", + "metadata": { + "type": "object", + "nullable": true, "metadata": { - "description": "Optional. whether product is published or not. Published products are discoverable by users of developer portal. Non published products are visible only to administrators. Default state of Product is notPublished. - notPublished or published." + "description": "Optional. The metadata attached to the record set." } }, - "subscriptionRequired": { - "type": "bool", - "defaultValue": false, + "ttl": { + "type": "int", + "defaultValue": 3600, "metadata": { - "description": "Optional. Whether a product subscription is required for accessing APIs included in this product. If true, the product is referred to as \"protected\" and a valid subscription key is required for a request to an API included in the product to succeed. If false, the product is referred to as \"open\" and requests to an API included in the product can be made without a subscription key. If property is omitted when creating a new product it's value is assumed to be true." + "description": "Optional. The TTL (time-to-live) of the records in the record set." } }, - "subscriptionsLimit": { - "type": "int", - "defaultValue": 1, + "txtRecords": { + "type": "array", + "nullable": true, "metadata": { - "description": "Optional. Whether the number of subscriptions a user can have to this product at the same time. Set to null or omit to allow unlimited per user subscriptions. Can be present only if subscriptionRequired property is present and has a value of false." + "description": "Optional. The list of TXT records in the record set." } }, - "terms": { - "type": "string", - "defaultValue": "", + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", "metadata": { - "description": "Optional. Product terms of use. Developers trying to subscribe to the product will be presented and required to accept these terms before they can complete the subscription process." + "description": "Optional. Array of role assignments to create." } } }, - "resources": [ - { - "type": "Microsoft.ApiManagement/service/products", - "apiVersion": "2022-08-01", - "name": "[format('{0}/{1}', parameters('apiManagementServiceName'), parameters('name'))]", - "properties": { - "description": "[parameters('description')]", - "displayName": "[parameters('displayName')]", - "terms": "[parameters('terms')]", - "subscriptionRequired": "[parameters('subscriptionRequired')]", - "approvalRequired": "[if(parameters('subscriptionRequired'), parameters('approvalRequired'), null())]", - "subscriptionsLimit": "[if(parameters('subscriptionRequired'), parameters('subscriptionsLimit'), null())]", - "state": "[parameters('state')]" + "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')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" }, - { - "copy": { - "name": "product_apis", - "count": "[length(parameters('apis'))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-Api-{1}', deployment().name, copyIndex())]", + "TXT": { + "type": "Microsoft.Network/privateDnsZones/TXT", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "apiManagementServiceName": { - "value": "[parameters('apiManagementServiceName')]" - }, - "name": { - "value": "[parameters('apis')[copyIndex()].name]" - }, - "productName": { - "value": "[parameters('name')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.34.1.11899", - "templateHash": "7782324617213267895" - }, - "name": "API Management Service Products APIs", - "description": "This module deploys an API Management Service Product API." - }, - "parameters": { - "apiManagementServiceName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent API Management service. Required if the template is used in a standalone deployment." - } - }, - "productName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent Product. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", - "metadata": { - "description": "Required. Name of the product API." - } - } - }, - "resources": [ - { - "type": "Microsoft.ApiManagement/service/products/apis", - "apiVersion": "2022-08-01", - "name": "[format('{0}/{1}/{2}', parameters('apiManagementServiceName'), parameters('productName'), parameters('name'))]" - } - ], - "outputs": { - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the product API." - }, - "value": "[resourceId('Microsoft.ApiManagement/service/products/apis', parameters('apiManagementServiceName'), parameters('productName'), parameters('name'))]" - }, - "name": { - "type": "string", - "metadata": { - "description": "The name of the product API." - }, - "value": "[parameters('name')]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group the product API was deployed into." - }, - "value": "[resourceGroup().name]" - } - } - } + "metadata": "[parameters('metadata')]", + "ttl": "[parameters('ttl')]", + "txtRecords": "[parameters('txtRecords')]" } }, - { + "TXT_roleAssignments": { "copy": { - "name": "product_groups", - "count": "[length(parameters('groups'))]" + "name": "TXT_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-Group-{1}', deployment().name, copyIndex())]", + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/TXT/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/TXT', parameters('privateDnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "apiManagementServiceName": { - "value": "[parameters('apiManagementServiceName')]" - }, - "name": { - "value": "[parameters('groups')[copyIndex()].name]" - }, - "productName": { - "value": "[parameters('name')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.34.1.11899", - "templateHash": "8814556585327002532" - }, - "name": "API Management Service Products Groups", - "description": "This module deploys an API Management Service Product Group." - }, - "parameters": { - "apiManagementServiceName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent API Management service. Required if the template is used in a standalone deployment." - } - }, - "productName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent Product. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", - "metadata": { - "description": "Required. Name of the product group." - } - } - }, - "resources": [ - { - "type": "Microsoft.ApiManagement/service/products/groups", - "apiVersion": "2022-08-01", - "name": "[format('{0}/{1}/{2}', parameters('apiManagementServiceName'), parameters('productName'), parameters('name'))]" - } - ], - "outputs": { - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the product group." - }, - "value": "[resourceId('Microsoft.ApiManagement/service/products/groups', parameters('apiManagementServiceName'), parameters('productName'), parameters('name'))]" - }, - "name": { - "type": "string", - "metadata": { - "description": "The name of the product group." - }, - "value": "[parameters('name')]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group the product group was deployed into." - }, - "value": "[resourceGroup().name]" - } - } - } - } + "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": [ + "TXT" + ] } - ], + }, "outputs": { - "resourceId": { + "name": { "type": "string", "metadata": { - "description": "The resource ID of the API management service product." + "description": "The name of the deployed TXT record." }, - "value": "[resourceId('Microsoft.ApiManagement/service/products', parameters('apiManagementServiceName'), parameters('name'))]" + "value": "[parameters('name')]" }, - "name": { + "resourceId": { "type": "string", "metadata": { - "description": "The name of the API management service product." + "description": "The resource ID of the deployed TXT record." }, - "value": "[parameters('name')]" + "value": "[resourceId('Microsoft.Network/privateDnsZones/TXT', parameters('privateDnsZoneName'), parameters('name'))]" }, "resourceGroupName": { "type": "string", "metadata": { - "description": "The resource group the API management service product was deployed into." + "description": "The resource group of the deployed TXT record." }, "value": "[resourceGroup().name]" - }, - "apiResourceIds": { - "type": "array", - "metadata": { - "description": "The Resources IDs of the API management service product APIs." - }, - "copy": { - "count": "[length(range(0, length(parameters('apis'))))]", - "input": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-Api-{1}', deployment().name, range(0, length(parameters('apis')))[copyIndex()])), '2022-09-01').outputs.resourceId.value]" - } - }, - "groupResourceIds": { - "type": "array", - "metadata": { - "description": "The Resources IDs of the API management service product groups." - }, - "copy": { - "count": "[length(range(0, length(parameters('groups'))))]", - "input": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-Group-{1}', deployment().name, range(0, length(parameters('groups')))[copyIndex()])), '2022-09-01').outputs.resourceId.value]" - } } } } }, "dependsOn": [ - "service", - "service_apis" + "privateDnsZone" ] }, - "service_subscriptions": { - "copy": { - "name": "service_subscriptions", - "count": "[length(parameters('subscriptions'))]" + "privateDnsZone_virtualNetworkLinks": { + "copy": { + "name": "privateDnsZone_virtualNetworkLinks", + "count": "[length(coalesce(parameters('virtualNetworkLinks'), createArray()))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-Apim-Subscription-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "name": "[format('{0}-PrivateDnsZone-VirtualNetworkLink-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "apiManagementServiceName": { + "privateDnsZoneName": { "value": "[parameters('name')]" }, "name": { - "value": "[parameters('subscriptions')[copyIndex()].name]" - }, - "displayName": { - "value": "[parameters('subscriptions')[copyIndex()].displayName]" - }, - "allowTracing": { - "value": "[tryGet(parameters('subscriptions')[copyIndex()], 'allowTracing')]" + "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'name'), format('{0}-vnetlink', last(split(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()].virtualNetworkResourceId, '/'))))]" }, - "ownerId": { - "value": "[tryGet(parameters('subscriptions')[copyIndex()], 'ownerId')]" + "virtualNetworkResourceId": { + "value": "[coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()].virtualNetworkResourceId]" }, - "primaryKey": { - "value": "[tryGet(parameters('subscriptions')[copyIndex()], 'primaryKey')]" + "location": { + "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'location'), 'global')]" }, - "scope": { - "value": "[tryGet(parameters('subscriptions')[copyIndex()], 'scope')]" + "registrationEnabled": { + "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'registrationEnabled'), false())]" }, - "secondaryKey": { - "value": "[tryGet(parameters('subscriptions')[copyIndex()], 'secondaryKey')]" + "tags": { + "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" }, - "state": { - "value": "[tryGet(parameters('subscriptions')[copyIndex()], 'state')]" + "resolutionPolicy": { + "value": "[tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'resolutionPolicy')]" } }, "template": { @@ -65858,173 +78365,158 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.34.1.11899", - "templateHash": "997401927729053094" + "version": "0.32.4.45862", + "templateHash": "15326596012552051215" }, - "name": "API Management Service Subscriptions", - "description": "This module deploys an API Management Service Subscription." + "name": "Private DNS Zone Virtual Network Link", + "description": "This module deploys a Private DNS Zone Virtual Network Link.", + "owner": "Azure/module-maintainers" }, "parameters": { - "allowTracing": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Optional. Determines whether tracing can be enabled." - } - }, - "displayName": { + "privateDnsZoneName": { "type": "string", - "maxLength": 100, "metadata": { - "description": "Required. API Management Service Subscriptions name. Must be 1 to 100 characters long." + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." } }, - "apiManagementServiceName": { + "name": { "type": "string", + "defaultValue": "[format('{0}-vnetlink', last(split(parameters('virtualNetworkResourceId'), '/')))]", "metadata": { - "description": "Conditional. The name of the parent API Management service. Required if the template is used in a standalone deployment." + "description": "Optional. The name of the virtual network link." } }, - "ownerId": { + "location": { "type": "string", - "nullable": true, + "defaultValue": "global", "metadata": { - "description": "Optional. User (user ID path) for whom subscription is being created in form /users/{userId}." + "description": "Optional. The location of the PrivateDNSZone. Should be global." } }, - "primaryKey": { - "type": "string", + "tags": { + "type": "object", "nullable": true, "metadata": { - "description": "Optional. Primary subscription key. If not specified during request key will be generated automatically." + "description": "Optional. Tags of the resource." } }, - "scope": { - "type": "string", - "defaultValue": "/apis", + "registrationEnabled": { + "type": "bool", + "defaultValue": false, "metadata": { - "description": "Optional. Scope type to choose between a product, \"allAPIs\" or a specific API. Scope like \"/products/{productId}\" or \"/apis\" or \"/apis/{apiId}\"." + "description": "Optional. Is auto-registration of virtual machine records in the virtual network in the Private DNS zone enabled?." } }, - "secondaryKey": { + "virtualNetworkResourceId": { "type": "string", - "nullable": true, "metadata": { - "description": "Optional. Secondary subscription key. If not specified during request key will be generated automatically." + "description": "Required. Link to another virtual network resource ID." } }, - "state": { + "resolutionPolicy": { "type": "string", "nullable": true, "metadata": { - "description": "Optional. Initial subscription state. If no value is specified, subscription is created with Submitted state. Possible states are \"*\" active \"?\" the subscription is active, \"*\" suspended \"?\" the subscription is blocked, and the subscriber cannot call any APIs of the product, * submitted ? the subscription request has been made by the developer, but has not yet been approved or rejected, * rejected ? the subscription request has been denied by an administrator, * cancelled ? the subscription has been cancelled by the developer or administrator, * expired ? the subscription reached its expiration date and was deactivated. - suspended, active, expired, submitted, rejected, cancelled." - } - }, - "name": { - "type": "string", - "metadata": { - "description": "Required. Subscription name." + "description": "Optional. The resolution policy on the virtual network link. Only applicable for virtual network links to privatelink zones, and for A,AAAA,CNAME queries. When set to `NxDomainRedirect`, Azure DNS resolver falls back to public resolution if private dns query resolution results in non-existent domain response. `Default` is configured as the default option." } } }, "resources": { - "service": { + "privateDnsZone": { "existing": true, - "type": "Microsoft.ApiManagement/service", - "apiVersion": "2023-05-01-preview", - "name": "[parameters('apiManagementServiceName')]" + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" }, - "subscription": { - "type": "Microsoft.ApiManagement/service/subscriptions", - "apiVersion": "2022-08-01", - "name": "[format('{0}/{1}', parameters('apiManagementServiceName'), parameters('name'))]", + "virtualNetworkLink": { + "type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks", + "apiVersion": "2024-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", "properties": { - "scope": "[parameters('scope')]", - "displayName": "[parameters('displayName')]", - "ownerId": "[parameters('ownerId')]", - "primaryKey": "[parameters('primaryKey')]", - "secondaryKey": "[parameters('secondaryKey')]", - "state": "[parameters('state')]", - "allowTracing": "[parameters('allowTracing')]" + "registrationEnabled": "[parameters('registrationEnabled')]", + "virtualNetwork": { + "id": "[parameters('virtualNetworkResourceId')]" + }, + "resolutionPolicy": "[parameters('resolutionPolicy')]" } } }, "outputs": { - "resourceId": { + "name": { "type": "string", "metadata": { - "description": "The resource ID of the API management service subscription." + "description": "The name of the deployed virtual network link." }, - "value": "[resourceId('Microsoft.ApiManagement/service/subscriptions', parameters('apiManagementServiceName'), parameters('name'))]" + "value": "[parameters('name')]" }, - "name": { + "resourceId": { "type": "string", "metadata": { - "description": "The name of the API management service subscription." + "description": "The resource ID of the deployed virtual network link." }, - "value": "[parameters('name')]" + "value": "[resourceId('Microsoft.Network/privateDnsZones/virtualNetworkLinks', parameters('privateDnsZoneName'), parameters('name'))]" }, "resourceGroupName": { "type": "string", "metadata": { - "description": "The resource group the API management service subscription was deployed into." + "description": "The resource group of the deployed virtual network link." }, "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('virtualNetworkLink', '2024-06-01', 'full').location]" } } } }, "dependsOn": [ - "service" + "privateDnsZone" ] } }, "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the API management service." - }, - "value": "[parameters('name')]" - }, - "resourceId": { + "resourceGroupName": { "type": "string", "metadata": { - "description": "The resource ID of the API management service." + "description": "The resource group the private DNS zone was deployed into." }, - "value": "[resourceId('Microsoft.ApiManagement/service', parameters('name'))]" + "value": "[resourceGroup().name]" }, - "resourceGroupName": { + "name": { "type": "string", "metadata": { - "description": "The resource group the API management service was deployed into." + "description": "The name of the private DNS zone." }, - "value": "[resourceGroup().name]" + "value": "[parameters('name')]" }, - "systemAssignedMIPrincipalId": { + "resourceId": { "type": "string", - "nullable": true, "metadata": { - "description": "The principal ID of the system assigned identity." + "description": "The resource ID of the private DNS zone." }, - "value": "[tryGet(tryGet(reference('service', '2024-05-01', 'full'), 'identity'), 'principalId')]" + "value": "[resourceId('Microsoft.Network/privateDnsZones', parameters('name'))]" }, "location": { "type": "string", "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('service', '2024-05-01', 'full').location]" + "value": "[reference('privateDnsZone', '2020-06-01', 'full').location]" } } } } }, - { - "condition": "[parameters('networkIsolation')]", + "cosmosDb": { "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "private-dns-apim-deployment", + "name": "[take(format('{0}-cosmosdb-deployment', variables('nameFormatted')), 64)]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -66032,15 +78524,63 @@ "mode": "Incremental", "parameters": { "name": { - "value": "privatelink.apim.windows.net" + "value": "[variables('nameFormatted')]" }, - "virtualNetworkLinks": { + "automaticFailover": { + "value": true + }, + "diagnosticSettings": { "value": [ { - "virtualNetworkResourceId": "[parameters('virtualNetworkResourceId')]" + "workspaceResourceId": "[parameters('logAnalyticsWorkspaceResourceId')]" + } + ] + }, + "disableKeyBasedMetadataWriteAccess": { + "value": true + }, + "disableLocalAuth": { + "value": true + }, + "location": { + "value": "[parameters('location')]" + }, + "minimumTlsVersion": { + "value": "Tls12" + }, + "defaultConsistencyLevel": { + "value": "Session" + }, + "networkRestrictions": { + "value": { + "networkAclBypass": "None", + "publicNetworkAccess": "[if(parameters('networkIsolation'), 'Disabled', 'Enabled')]" + } + }, + "privateEndpoints": "[if(parameters('networkIsolation'), createObject('value', createArray(createObject('privateDnsZoneGroup', createObject('privateDnsZoneGroupConfigs', createArray(createObject('privateDnsZoneResourceId', reference('privateDnsZone').outputs.resourceId.value))), 'service', 'Sql', 'subnetResourceId', parameters('virtualNetworkSubnetResourceId')))), createObject('value', createArray()))]", + "sqlDatabases": { + "value": "[parameters('databases')]" + }, + "roleAssignments": { + "value": "[parameters('roleAssignments')]" + }, + "sqlRoleDefinitions": { + "value": [ + { + "name": "[guid(resourceGroup().id, variables('nameFormatted'), 'custom-sql-role')]", + "roleType": "CustomRole", + "roleName": "Cosmos DB Data Reader Writer", + "dataAction": [ + "Microsoft.DocumentDB/databaseAccounts/readMetadata", + "Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/items/*", + "Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/*" + ] } ] }, + "sqlRoleAssignmentsPrincipalIds": { + "value": "[parameters('sqlRoleAssignmentsPrincipalIds')]" + }, "tags": { "value": "[parameters('tags')]" } @@ -66052,735 +78592,1174 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "83178825086050429" + "version": "0.34.44.8038", + "templateHash": "10038898747803596366" }, - "name": "Private DNS Zones", - "description": "This module deploys a Private DNS zone.", - "owner": "Azure/module-maintainers" + "name": "DocumentDB Database Accounts", + "description": "This module deploys a DocumentDB Database Account." }, "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." + "privateEndpointOutputType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the private endpoint." + } + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the private endpoint." + } + }, + "groupId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "The group Id for the private endpoint Group." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "FQDN that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "A list of private IP addresses of the private endpoint." + } + } } }, - "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\"." - } + "metadata": { + "description": "The custom DNS configurations of the private endpoint." + } + }, + "networkInterfaceResourceIds": { + "type": "array", + "items": { + "type": "string" }, - "conditionVersion": { - "type": "string", - "allowedValues": [ - "2.0" - ], - "nullable": true, - "metadata": { - "description": "Optional. Version of the condition." + "metadata": { + "description": "The IDs of the network interfaces associated with the private endpoint." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for the private endpoint output." + } + }, + "failoverLocationType": { + "type": "object", + "properties": { + "failoverPriority": { + "type": "int", + "metadata": { + "description": "Required. The failover priority of the region. A failover priority of 0 indicates a write region. The maximum value for a failover priority = (total number of regions - 1). Failover priority values must be unique for each of the regions in which the database account exists." + } + }, + "isZoneRedundant": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Default to true. Flag to indicate whether or not this region is an AvailabilityZone region." + } + }, + "locationName": { + "type": "string", + "metadata": { + "description": "Required. The name of the region." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for the failover location." + } + }, + "sqlRoleDefinitionType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the SQL Role Definition." + } + }, + "dataAction": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. An array of data actions that are allowed." + } + }, + "roleName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. A user-friendly name for the Role Definition. Must be unique for the database account." + } + }, + "roleType": { + "type": "string", + "allowedValues": [ + "BuiltInRole", + "CustomRole" + ], + "nullable": true, + "metadata": { + "description": "Optional. Indicates whether the Role Definition was built-in or user created." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for the SQL Role Definitions." + } + }, + "sqlDatabaseType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the SQL database ." + } + }, + "throughput": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Default to 400. Request units per second. Will be ignored if autoscaleSettingsMaxThroughput is used. Setting throughput at the database level is only recommended for development/test or when workload across all containers in the shared throughput database is uniform. For best performance for large production workloads, it is recommended to set dedicated throughput (autoscale or manual) at the container level and not at the database level." + } + }, + "autoscaleSettingsMaxThroughput": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Specifies the Autoscale settings and represents maximum throughput, the resource can scale up to. The autoscale throughput should have valid throughput values between 1000 and 1000000 inclusive in increments of 1000. If value is set to null, then autoscale will be disabled. Setting throughput at the database level is only recommended for development/test or when workload across all containers in the shared throughput database is uniform. For best performance for large production workloads, it is recommended to set dedicated throughput (autoscale or manual) at the container level and not at the database level." + } + }, + "containers": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the container." + } + }, + "paths": { + "type": "array", + "items": { + "type": "string" + }, + "minLength": 1, + "maxLength": 3, + "metadata": { + "description": "Required. List of paths using which data within the container can be partitioned. For kind=MultiHash it can be up to 3. For anything else it needs to be exactly 1." + } + }, + "analyticalStorageTtl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Default to 0. Indicates how long data should be retained in the analytical store, for a container. Analytical store is enabled when ATTL is set with a value other than 0. If the value is set to -1, the analytical store retains all historical data, irrespective of the retention of the data in the transactional store." + } + }, + "autoscaleSettingsMaxThroughput": { + "type": "int", + "nullable": true, + "maxValue": 1000000, + "metadata": { + "description": "Optional. Specifies the Autoscale settings and represents maximum throughput, the resource can scale up to. The autoscale throughput should have valid throughput values between 1000 and 1000000 inclusive in increments of 1000. If value is set to null, then autoscale will be disabled. For best performance for large production workloads, it is recommended to set dedicated throughput (autoscale or manual) at the container level." + } + }, + "conflictResolutionPolicy": { + "type": "object", + "properties": { + "conflictResolutionPath": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The conflict resolution path in the case of LastWriterWins mode. Required if `mode` is set to 'LastWriterWins'." + } + }, + "conflictResolutionProcedure": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The procedure to resolve conflicts in the case of custom mode. Required if `mode` is set to 'Custom'." + } + }, + "mode": { + "type": "string", + "allowedValues": [ + "Custom", + "LastWriterWins" + ], + "metadata": { + "description": "Required. Indicates the conflict resolution mode." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The conflict resolution policy for the container. Conflicts and conflict resolution policies are applicable if the Azure Cosmos DB account is configured with multiple write regions." + } + }, + "defaultTtl": { + "type": "int", + "nullable": true, + "minValue": -1, + "maxValue": 2147483647, + "metadata": { + "description": "Optional. Default to -1. Default time to live (in seconds). With Time to Live or TTL, Azure Cosmos DB provides the ability to delete items automatically from a container after a certain time period. If the value is set to \"-1\", it is equal to infinity, and items don't expire by default." + } + }, + "indexingPolicy": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Indexing policy of the container." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "Hash", + "MultiHash" + ], + "nullable": true, + "metadata": { + "description": "Optional. Default to Hash. Indicates the kind of algorithm used for partitioning." + } + }, + "version": { + "type": "int", + "allowedValues": [ + 1, + 2 + ], + "nullable": true, + "metadata": { + "description": "Optional. Default to 1 for Hash and 2 for MultiHash - 1 is not allowed for MultiHash. Version of the partition key definition." + } + }, + "throughput": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Default to 400. Request Units per second. Will be ignored if autoscaleSettingsMaxThroughput is used." + } + }, + "uniqueKeyPolicyKeys": { + "type": "array", + "items": { + "type": "object", + "properties": { + "paths": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. List of paths must be unique for each document in the Azure Cosmos DB service." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The unique key policy configuration containing a list of unique keys that enforces uniqueness constraint on documents in the collection in the Azure Cosmos DB service." + } + } } }, - "delegatedManagedIdentityResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The Resource Id of the delegated managed identity resource." - } + "nullable": true, + "metadata": { + "description": "Optional. Array of containers to deploy in the SQL database." } } }, - "nullable": true + "metadata": { + "__bicep_export!": true, + "description": "The type for the SQL database." + } }, - "lockType": { + "secretsExportConfigurationType": { "type": "object", "properties": { - "name": { + "keyVaultResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the key vault where to store the secrets of this module." + } + }, + "primaryWriteKeySecretName": { "type": "string", "nullable": true, "metadata": { - "description": "Optional. Specify the name of lock." + "description": "Optional. The primary write key secret name to create." } }, - "kind": { + "primaryReadOnlyKeySecretName": { "type": "string", - "allowedValues": [ - "CanNotDelete", - "None", - "ReadOnly" - ], "nullable": true, "metadata": { - "description": "Optional. Specify the type of lock." + "description": "Optional. The primary readonly key secret name to create." } - } - }, - "nullable": true - }, - "aType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } - }, + }, + "primaryWriteConnectionStringSecretName": { + "type": "string", + "nullable": true, "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } - }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "aRecords": { - "type": "array", - "items": { - "type": "object", - "properties": { - "ipv4Address": { - "type": "string", - "metadata": { - "description": "Required. The IPv4 address of this A record." - } - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. The list of A records in the record set." - } + "description": "Optional. The primary write connection string secret name to create." + } + }, + "primaryReadonlyConnectionStringSecretName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The primary readonly connection string secret name to create." + } + }, + "secondaryWriteKeySecretName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The primary write key secret name to create." + } + }, + "secondaryReadonlyKeySecretName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The primary readonly key secret name to create." + } + }, + "secondaryWriteConnectionStringSecretName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The primary write connection string secret name to create." + } + }, + "secondaryReadonlyConnectionStringSecretName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The primary readonly connection string secret name to create." } } }, - "nullable": true + "metadata": { + "__bicep_export!": true, + "description": "The type for the secrets export configuration." + } }, - "aaaaType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } - }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "aaaaRecords": { - "type": "array", - "items": { - "type": "object", - "properties": { - "ipv6Address": { - "type": "string", - "metadata": { - "description": "Required. The IPv6 address of this AAAA record." - } - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. The list of AAAA records in the record set." - } - } + "secretsOutputType": { + "type": "object", + "properties": {}, + "additionalProperties": { + "$ref": "#/definitions/secretSetType", + "metadata": { + "description": "An exported secret's references." } }, - "nullable": true + "metadata": { + "__bicep_export!": true, + "description": "The type for the secrets output." + } }, - "cnameType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } + "networkRestrictionType": { + "type": "object", + "properties": { + "ipRules": { + "type": "array", + "items": { + "type": "string" }, + "nullable": true, "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } - }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "cnameRecord": { + "description": "Optional. A single IPv4 address or a single IPv4 address range in CIDR format. Provided IPs must be well-formatted and cannot be contained in one of the following ranges: 10.0.0.0/8, 100.64.0.0/10, 172.16.0.0/12, 192.168.0.0/16, since these are not enforceable by the IP address filter. Example of valid inputs: \"23.40.210.245\" or \"23.40.210.0/8\"." + } + }, + "networkAclBypass": { + "type": "string", + "allowedValues": [ + "AzureServices", + "None" + ], + "nullable": true, + "metadata": { + "description": "Optional. Default to None. Specifies the network ACL bypass for Azure services." + } + }, + "publicNetworkAccess": { + "type": "string", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "nullable": true, + "metadata": { + "description": "Optional. Default to Disabled. Whether requests from Public Network are allowed." + } + }, + "virtualNetworkRules": { + "type": "array", + "items": { "type": "object", "properties": { - "cname": { + "subnetResourceId": { "type": "string", "metadata": { - "description": "Required. The canonical name of the CNAME record." + "description": "Required. Resource ID of a subnet." } } - }, - "nullable": true, - "metadata": { - "description": "Optional. The CNAME record in the record set." } + }, + "nullable": true, + "metadata": { + "description": "Optional. List of Virtual Network ACL rules configured for the Cosmos DB account.." } } }, - "nullable": true + "metadata": { + "__bicep_export!": true, + "description": "The type for the network restriction." + } }, - "mxType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } - }, + "_1.privateEndpointCustomDnsConfigType": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } - }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } + "description": "Optional. FQDN that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" }, - "mxRecords": { - "type": "array", - "items": { - "type": "object", - "properties": { - "exchange": { - "type": "string", - "metadata": { - "description": "Required. The domain name of the mail host for this MX record." - } - }, - "preference": { - "type": "int", - "metadata": { - "description": "Required. The preference value for this MX record." - } - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. The list of MX records in the record set." - } + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." } } }, - "nullable": true + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } }, - "ptrType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } - }, + "_1.privateEndpointIpConfigurationType": { + "type": "object", + "properties": { + "name": { + "type": "string", "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } - }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "ptrRecords": { - "type": "array", - "items": { - "type": "object", - "properties": { - "ptrdname": { - "type": "string", - "metadata": { - "description": "Required. The PTR target domain name for this PTR record." - } - } + "description": "Required. The name of the resource that is unique within a resource group." + } + }, + "properties": { + "type": "object", + "properties": { + "groupId": { + "type": "string", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." } }, - "nullable": true, - "metadata": { - "description": "Optional. The list of PTR records in the record set." + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } } + }, + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." } } }, - "nullable": true + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } }, - "soaType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } - }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "soaRecord": { + "_1.privateEndpointPrivateDnsZoneGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { "type": "object", "properties": { - "email": { + "name": { "type": "string", + "nullable": true, "metadata": { - "description": "Required. The email contact for this SOA record." + "description": "Optional. The name of the private DNS Zone Group config." } }, - "expireTime": { - "type": "int", + "privateDnsZoneResourceId": { + "type": "string", "metadata": { - "description": "Required. The expire time for this SOA record." + "description": "Required. The resource id of the private DNS zone." } - }, - "host": { + } + } + }, + "metadata": { + "description": "Required. The private DNS Zone Groups to associate the Private Endpoint. A DNS Zone Group can support up to 5 DNS zones." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "diagnosticSettingFullType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { "type": "string", + "nullable": true, "metadata": { - "description": "Required. The domain name of the authoritative name server for this SOA record." + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." } }, - "minimumTtl": { - "type": "int", + "categoryGroup": { + "type": "string", + "nullable": true, "metadata": { - "description": "Required. The minimum value for this SOA record. By convention this is used to determine the negative caching duration." + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." } }, - "refreshTime": { - "type": "int", + "enabled": { + "type": "bool", + "nullable": true, "metadata": { - "description": "Required. The refresh value for this SOA record." + "description": "Optional. Enable or disable the category explicitly. Default is `true`." } - }, - "retryTime": { - "type": "int", + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", "metadata": { - "description": "Required. The retry time for this SOA record." + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." } }, - "serialNumber": { - "type": "int", + "enabled": { + "type": "bool", + "nullable": true, "metadata": { - "description": "Required. The serial number for this SOA record." + "description": "Optional. Enable or disable the category explicitly. Default is `true`." } } - }, - "nullable": true, - "metadata": { - "description": "Optional. The SOA record in the record set." } + }, + "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." } } }, - "nullable": true + "metadata": { + "description": "An AVM-aligned type for a diagnostic setting. To be used if both logs & metrics are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } }, - "srvType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } - }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } - }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of 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.5.1" + } + } + }, + "managedIdentityAllType": { + "type": "object", + "properties": { + "systemAssigned": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enables system assigned managed identity on the resource." + } + }, + "userAssignedResourceIds": { + "type": "array", + "items": { + "type": "string" }, - "srvRecords": { - "type": "array", - "items": { - "type": "object", - "properties": { - "priority": { - "type": "int", - "metadata": { - "description": "Required. The priority value for this SRV record." - } - }, - "weight": { - "type": "int", - "metadata": { - "description": "Required. The weight value for this SRV record." - } - }, - "port": { - "type": "int", - "metadata": { - "description": "Required. The port value for this SRV record." - } - }, - "target": { - "type": "string", - "metadata": { - "description": "Required. The target domain name for this SRV record." - } - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. The list of SRV records in the record set." - } + "nullable": true, + "metadata": { + "description": "Optional. The resource ID(s) to assign to the resource. Required if a user assigned identity is used for encryption." } } }, - "nullable": true + "metadata": { + "description": "An AVM-aligned type for a managed identity configuration. To be used if both a system-assigned & user-assigned identities are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } }, - "txtType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } + "privateEndpointMultiServiceType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private endpoint." + } + }, + "location": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The location to deploy the private endpoint to." + } + }, + "privateLinkServiceConnectionName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private link connection to create." + } + }, + "service": { + "type": "string", + "metadata": { + "description": "Required. The subresource to deploy the private endpoint for. For example \"blob\", \"table\", \"queue\" or \"file\" for a Storage Account's Private Endpoints." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + } + }, + "resourceGroupResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the Resource Group the Private Endpoint will be created in. If not specified, the Resource Group of the provided Virtual Network Subnet is used." + } + }, + "privateDnsZoneGroup": { + "$ref": "#/definitions/_1.privateEndpointPrivateDnsZoneGroupType", + "nullable": true, + "metadata": { + "description": "Optional. The private DNS zone group to configure for the private endpoint." + } + }, + "isManualConnection": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. If Manual Private Link Connection is required." + } + }, + "manualConnectionRequestMessage": { + "type": "string", + "nullable": true, + "maxLength": 140, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with the manual connection request." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/_1.privateEndpointCustomDnsConfigType" }, + "nullable": true, "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } + "description": "Optional. Custom DNS configurations." + } + }, + "ipConfigurations": { + "type": "array", + "items": { + "$ref": "#/definitions/_1.privateEndpointIpConfigurationType" }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } + "nullable": true, + "metadata": { + "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." + } + }, + "applicationSecurityGroupResourceIds": { + "type": "array", + "items": { + "type": "string" }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } + "nullable": true, + "metadata": { + "description": "Optional. Application security groups in which the private endpoint IP configuration is included." + } + }, + "customNetworkInterfaceName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The custom name of the network interface attached to the private endpoint." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" }, - "txtRecords": { - "type": "array", - "items": { - "type": "object", - "properties": { - "value": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. The text value of this TXT record." - } - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. The list of TXT records in the record set." - } + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." + } + }, + "enableTelemetry": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a private endpoint. To be used if the private endpoint's default service / groupId can NOT be assumed (i.e., for services that have more than one subresource, like Storage Account with Blob (blob, table, queue, file, ...).", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "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." } } }, - "nullable": true + "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.5.1" + } + } }, - "virtualNetworkLinkType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "minLength": 1, - "maxLength": 80, - "metadata": { - "description": "Optional. The resource name." - } - }, - "virtualNetworkResourceId": { - "type": "string", - "metadata": { - "description": "Required. The resource ID of the virtual network to link." - } - }, - "location": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The Azure Region where the resource lives." - } - }, - "registrationEnabled": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Is auto-registration of virtual machine records in the virtual network in the Private DNS zone enabled?." - } - }, - "tags": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Resource tags." - } - }, - "resolutionPolicy": { - "type": "string", - "allowedValues": [ - "Default", - "NxDomainRedirect" - ], - "nullable": true, - "metadata": { - "description": "Optional. The resolution type of the private-dns-zone fallback machanism." - } + "secretSetType": { + "type": "object", + "properties": { + "secretResourceId": { + "type": "string", + "metadata": { + "description": "The resourceId of the exported secret." + } + }, + "secretUri": { + "type": "string", + "metadata": { + "description": "The secret URI of the exported secret." } } }, - "nullable": true + "metadata": { + "description": "The type for the secret set.", + "__bicep_imported_from!": { + "sourceTemplate": "modules/keyVaultExport.bicep" + } + } } }, "parameters": { "name": { "type": "string", "metadata": { - "description": "Required. Private DNS zone name." + "description": "Required. Name of the Database Account." } }, - "a": { - "$ref": "#/definitions/aType", + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", "metadata": { - "description": "Optional. Array of A records." + "description": "Optional. Default to current resource group scope location. Location for all resources." } }, - "aaaa": { - "$ref": "#/definitions/aaaaType", + "tags": { + "type": "object", + "nullable": true, "metadata": { - "description": "Optional. Array of AAAA records." + "description": "Optional. Tags of the Database Account resource." } }, - "cname": { - "$ref": "#/definitions/cnameType", + "managedIdentities": { + "$ref": "#/definitions/managedIdentityAllType", + "nullable": true, "metadata": { - "description": "Optional. Array of CNAME records." + "description": "Optional. The managed identity definition for this resource." } }, - "mx": { - "$ref": "#/definitions/mxType", + "databaseAccountOfferType": { + "type": "string", + "defaultValue": "Standard", + "allowedValues": [ + "Standard" + ], "metadata": { - "description": "Optional. Array of MX records." + "description": "Optional. Default to Standard. The offer type for the Azure Cosmos DB database account." } }, - "ptr": { - "$ref": "#/definitions/ptrType", + "locations": { + "type": "array", + "items": { + "$ref": "#/definitions/failoverLocationType" + }, + "defaultValue": [], "metadata": { - "description": "Optional. Array of PTR records." + "description": "Optional. Default to the location where the account is deployed. Locations enabled for the Cosmos DB account." } }, - "soa": { - "$ref": "#/definitions/soaType", + "defaultConsistencyLevel": { + "type": "string", + "defaultValue": "Session", + "allowedValues": [ + "Eventual", + "ConsistentPrefix", + "Session", + "BoundedStaleness", + "Strong" + ], "metadata": { - "description": "Optional. Array of SOA records." + "description": "Optional. Default to Session. The default consistency level of the Cosmos DB account." } }, - "srv": { - "$ref": "#/definitions/srvType", + "disableLocalAuth": { + "type": "bool", + "defaultValue": true, "metadata": { - "description": "Optional. Array of SRV records." + "description": "Optional. Default to true. Opt-out of local authentication and ensure only MSI and AAD can be used exclusively for authentication." } }, - "txt": { - "$ref": "#/definitions/txtType", + "enableAnalyticalStorage": { + "type": "bool", + "defaultValue": false, "metadata": { - "description": "Optional. Array of TXT records." + "description": "Optional. Default to false. Flag to indicate whether to enable storage analytics." } }, - "virtualNetworkLinks": { - "$ref": "#/definitions/virtualNetworkLinkType", + "automaticFailover": { + "type": "bool", + "defaultValue": true, "metadata": { - "description": "Optional. Array of custom objects describing vNet links of the DNS zone. Each object should contain properties 'virtualNetworkResourceId' and 'registrationEnabled'. The 'vnetResourceId' is a resource ID of a vNet to link, 'registrationEnabled' (bool) enables automatic DNS registration in the zone for the linked vNet." + "description": "Optional. Default to true. Enable automatic failover for regions." } }, - "location": { + "enableFreeTier": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Default to false. Flag to indicate whether Free Tier is enabled." + } + }, + "enableMultipleWriteLocations": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Default to false. Enables the account to write in multiple locations. Periodic backup must be used if enabled." + } + }, + "disableKeyBasedMetadataWriteAccess": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Default to true. Disable write operations on metadata resources (databases, containers, throughput) via account keys." + } + }, + "maxStalenessPrefix": { + "type": "int", + "defaultValue": 100000, + "minValue": 1, + "maxValue": 2147483647, + "metadata": { + "description": "Optional. Default to 100000. Max stale requests. Required for BoundedStaleness. Valid ranges, Single Region: 10 to 1000000. Multi Region: 100000 to 1000000." + } + }, + "maxIntervalInSeconds": { + "type": "int", + "defaultValue": 300, + "minValue": 5, + "maxValue": 86400, + "metadata": { + "description": "Optional. Default to 300. Max lag time (minutes). Required for BoundedStaleness. Valid ranges, Single Region: 5 to 84600. Multi Region: 300 to 86400." + } + }, + "serverVersion": { "type": "string", - "defaultValue": "global", + "defaultValue": "4.2", + "allowedValues": [ + "3.2", + "3.6", + "4.0", + "4.2", + "5.0", + "6.0", + "7.0" + ], "metadata": { - "description": "Optional. The location of the PrivateDNSZone. Should be global." + "description": "Optional. Default to 4.2. Specifies the MongoDB server version to use." } }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", + "sqlDatabases": { + "type": "array", + "items": { + "$ref": "#/definitions/sqlDatabaseType" + }, + "defaultValue": [], "metadata": { - "description": "Optional. Array of role assignments to create." + "description": "Optional. SQL Databases configurations." } }, - "tags": { - "type": "object", + "sqlRoleAssignmentsPrincipalIds": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. SQL Role Definitions configurations." + } + }, + "sqlRoleDefinitions": { + "type": "array", + "items": { + "$ref": "#/definitions/sqlRoleDefinitionType" + }, "nullable": true, "metadata": { - "description": "Optional. Tags of the resource." + "description": "Optional. SQL Role Definitions configurations." } }, - "lock": { - "$ref": "#/definitions/lockType", + "mongodbDatabases": { + "type": "array", + "defaultValue": [], "metadata": { - "description": "Optional. The lock settings of the service." + "description": "Optional. MongoDB Databases configurations." + } + }, + "gremlinDatabases": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Gremlin Databases configurations." + } + }, + "tables": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Table configurations." } }, "enableTelemetry": { @@ -66789,620 +79768,374 @@ "metadata": { "description": "Optional. Enable/Disable usage telemetry for module." } + }, + "totalThroughputLimit": { + "type": "int", + "defaultValue": -1, + "metadata": { + "description": "Optional. Default to unlimited. The total throughput limit imposed on this Cosmos DB account (RU/s)." + } + }, + "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." + } + }, + "diagnosticSettings": { + "type": "array", + "items": { + "$ref": "#/definitions/diagnosticSettingFullType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "capabilitiesToAdd": { + "type": "array", + "items": { + "type": "string" + }, + "defaultValue": [], + "allowedValues": [ + "EnableCassandra", + "EnableTable", + "EnableGremlin", + "EnableMongo", + "DisableRateLimitingResponses", + "EnableServerless", + "EnableNoSQLVectorSearch", + "EnableNoSQLFullTextSearch", + "EnableMaterializedViews", + "DeleteAllItemsByPartitionKey" + ], + "metadata": { + "description": "Optional. List of Cosmos DB capabilities for the account. THE DeleteAllItemsByPartitionKey VALUE USED IN THIS PARAMETER IS USED FOR A PREVIEW SERVICE/FEATURE, MICROSOFT MAY NOT PROVIDE SUPPORT FOR THIS, PLEASE CHECK THE PRODUCT DOCS FOR CLARIFICATION." + } + }, + "backupPolicyType": { + "type": "string", + "defaultValue": "Continuous", + "allowedValues": [ + "Periodic", + "Continuous" + ], + "metadata": { + "description": "Optional. Default to Continuous. Describes the mode of backups. Periodic backup must be used if multiple write locations are used." + } + }, + "backupPolicyContinuousTier": { + "type": "string", + "defaultValue": "Continuous30Days", + "allowedValues": [ + "Continuous30Days", + "Continuous7Days" + ], + "metadata": { + "description": "Optional. Default to Continuous30Days. Configuration values for continuous mode backup." + } + }, + "backupIntervalInMinutes": { + "type": "int", + "defaultValue": 240, + "minValue": 60, + "maxValue": 1440, + "metadata": { + "description": "Optional. Default to 240. An integer representing the interval in minutes between two backups. Only applies to periodic backup type." + } + }, + "backupRetentionIntervalInHours": { + "type": "int", + "defaultValue": 8, + "minValue": 2, + "maxValue": 720, + "metadata": { + "description": "Optional. Default to 8. An integer representing the time (in hours) that each backup is retained. Only applies to periodic backup type." + } + }, + "backupStorageRedundancy": { + "type": "string", + "defaultValue": "Local", + "allowedValues": [ + "Geo", + "Local", + "Zone" + ], + "metadata": { + "description": "Optional. Default to Local. Enum to indicate type of backup residency. Only applies to periodic backup type." + } + }, + "privateEndpoints": { + "type": "array", + "items": { + "$ref": "#/definitions/privateEndpointMultiServiceType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible." + } + }, + "secretsExportConfiguration": { + "$ref": "#/definitions/secretsExportConfigurationType", + "nullable": true, + "metadata": { + "description": "Optional. Key vault reference and secret settings for the module's secrets export." + } + }, + "networkRestrictions": { + "$ref": "#/definitions/networkRestrictionType", + "defaultValue": { + "ipRules": [], + "virtualNetworkRules": [], + "publicNetworkAccess": "Disabled" + }, + "metadata": { + "description": "Optional. The network configuration of this module. Defaults to `{ ipRules: [], virtualNetworkRules: [], publicNetworkAccess: 'Disabled' }`." + } + }, + "minimumTlsVersion": { + "type": "string", + "defaultValue": "Tls12", + "allowedValues": [ + "Tls12" + ], + "metadata": { + "description": "Optional. Default to TLS 1.2. Enum to indicate the minimum allowed TLS version. Azure Cosmos DB for MongoDB RU and Apache Cassandra only work with TLS 1.2 or later." + } } }, "variables": { "copy": [ + { + "name": "databaseAccount_locations", + "count": "[length(parameters('locations'))]", + "input": { + "failoverPriority": "[parameters('locations')[copyIndex('databaseAccount_locations')].failoverPriority]", + "locationName": "[parameters('locations')[copyIndex('databaseAccount_locations')].locationName]", + "isZoneRedundant": "[coalesce(tryGet(parameters('locations')[copyIndex('databaseAccount_locations')], 'isZoneRedundant'), true())]" + } + }, + { + "name": "capabilities", + "count": "[length(parameters('capabilitiesToAdd'))]", + "input": { + "name": "[parameters('capabilitiesToAdd')[copyIndex('capabilities')]]" + } + }, + { + "name": "ipRules", + "count": "[length(coalesce(tryGet(parameters('networkRestrictions'), 'ipRules'), createArray()))]", + "input": { + "ipAddressOrRange": "[coalesce(tryGet(parameters('networkRestrictions'), 'ipRules'), createArray())[copyIndex('ipRules')]]" + } + }, + { + "name": "virtualNetworkRules", + "count": "[length(coalesce(tryGet(parameters('networkRestrictions'), 'virtualNetworkRules'), createArray()))]", + "input": { + "id": "[coalesce(tryGet(parameters('networkRestrictions'), 'virtualNetworkRules'), createArray())[copyIndex('virtualNetworkRules')].subnetResourceId]", + "ignoreMissingVnetServiceEndpoint": false + } + }, { "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')]", - "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": "2024-03-01", - "name": "[format('46d3xbcp.res.network-privatednszone.{0}.{1}', replace('0.7.0', '.', '-'), 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" - } - } - } - } - }, - "privateDnsZone": { - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('name')]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]" - }, - "privateDnsZone_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/privateDnsZones/{0}', parameters('name'))]", - "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", - "properties": { - "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", - "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + "enableReferencedModulesTelemetry": false, + "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", + "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned,UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', null())), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", + "consistencyPolicy": { + "Eventual": { + "defaultConsistencyLevel": "Eventual" }, - "dependsOn": [ - "privateDnsZone" - ] - }, - "privateDnsZone_roleAssignments": { - "copy": { - "name": "privateDnsZone_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + "ConsistentPrefix": { + "defaultConsistencyLevel": "ConsistentPrefix" }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}', parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones', 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')]" + "Session": { + "defaultConsistencyLevel": "Session" + }, + "BoundedStaleness": { + "defaultConsistencyLevel": "BoundedStaleness", + "maxStalenessPrefix": "[parameters('maxStalenessPrefix')]", + "maxIntervalInSeconds": "[parameters('maxIntervalInSeconds')]" }, - "dependsOn": [ - "privateDnsZone" - ] + "Strong": { + "defaultConsistencyLevel": "Strong" + } }, - "privateDnsZone_A": { - "copy": { - "name": "privateDnsZone_A", - "count": "[length(coalesce(parameters('a'), createArray()))]" - }, + "defaultFailoverLocation": [ + { + "failoverPriority": 0, + "locationName": "[parameters('location')]", + "isZoneRedundant": true + } + ], + "kind": "[if(or(not(empty(parameters('sqlDatabases'))), not(empty(parameters('gremlinDatabases')))), 'GlobalDocumentDB', if(not(empty(parameters('mongodbDatabases'))), 'MongoDB', 'GlobalDocumentDB'))]", + "backupPolicy": "[if(equals(parameters('backupPolicyType'), 'Continuous'), createObject('type', parameters('backupPolicyType'), 'continuousModeProperties', createObject('tier', parameters('backupPolicyContinuousTier'))), createObject('type', parameters('backupPolicyType'), 'periodicModeProperties', createObject('backupIntervalInMinutes', parameters('backupIntervalInMinutes'), 'backupRetentionIntervalInHours', parameters('backupRetentionIntervalInHours'), 'backupStorageRedundancy', parameters('backupStorageRedundancy'))))]", + "databaseAccountProperties": "[union(createObject('databaseAccountOfferType', parameters('databaseAccountOfferType'), 'backupPolicy', variables('backupPolicy'), 'capabilities', variables('capabilities'), 'minimalTlsVersion', parameters('minimumTlsVersion'), 'capacity', createObject('totalThroughputLimit', parameters('totalThroughputLimit'))), if(or(or(or(not(empty(parameters('sqlDatabases'))), not(empty(parameters('mongodbDatabases')))), not(empty(parameters('gremlinDatabases')))), not(empty(parameters('tables')))), createObject('consistencyPolicy', variables('consistencyPolicy')[parameters('defaultConsistencyLevel')], 'enableMultipleWriteLocations', parameters('enableMultipleWriteLocations'), 'locations', if(empty(variables('databaseAccount_locations')), variables('defaultFailoverLocation'), variables('databaseAccount_locations')), 'ipRules', variables('ipRules'), 'virtualNetworkRules', variables('virtualNetworkRules'), 'networkAclBypass', coalesce(tryGet(parameters('networkRestrictions'), 'networkAclBypass'), 'None'), 'publicNetworkAccess', coalesce(tryGet(parameters('networkRestrictions'), 'publicNetworkAccess'), 'Disabled'), 'isVirtualNetworkFilterEnabled', or(not(empty(variables('ipRules'))), not(empty(variables('virtualNetworkRules')))), 'enableFreeTier', parameters('enableFreeTier'), 'enableAutomaticFailover', parameters('automaticFailover'), 'enableAnalyticalStorage', parameters('enableAnalyticalStorage')), createObject()), if(or(not(empty(parameters('sqlDatabases'))), not(empty(parameters('tables')))), createObject('disableLocalAuth', parameters('disableLocalAuth'), 'disableKeyBasedMetadataWriteAccess', parameters('disableKeyBasedMetadataWriteAccess')), createObject()), if(not(empty(parameters('mongodbDatabases'))), createObject('apiProperties', createObject('serverVersion', parameters('serverVersion'))), createObject()))]", + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Cosmos DB Account Reader Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fbdf93bf-df7d-467e-a4d2-9458aa1360c8')]", + "Cosmos DB Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa')]", + "CosmosBackupOperator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'db7b14f2-5adf-42da-9f96-f2ee17bab5cb')]", + "CosmosRestoreOperator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5432c526-bc82-444a-b7ba-57c5b0b5b34f')]", + "DocumentDB Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5bd9cd88-fe45-4216-938b-f97437e15450')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-ARecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "apiVersion": "2024-07-01", + "name": "[format('46d3xbcp.res.documentdb-databaseaccount.{0}.{1}', replace('0.13.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, "mode": "Incremental", - "parameters": { - "privateDnsZoneName": { - "value": "[parameters('name')]" - }, - "name": { - "value": "[coalesce(parameters('a'), createArray())[copyIndex()].name]" - }, - "aRecords": { - "value": "[tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'aRecords')]" - }, - "metadata": { - "value": "[tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'metadata')]" - }, - "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'ttl'), 3600)]" - }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'roleAssignments')]" - } - }, "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.32.4.45862", - "templateHash": "2531120132215940282" - }, - "name": "Private DNS Zone A record", - "description": "This module deploys a Private DNS Zone A record.", - "owner": "Azure/module-maintainers" - }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } - } - } - }, - "nullable": true - } - }, - "parameters": { - "privateDnsZoneName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the A record." - } - }, - "aRecords": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. The list of A records in the record set." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata attached to the record set." - } - }, - "ttl": { - "type": "int", - "defaultValue": 3600, - "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - } - }, - "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')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", - "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" - } - }, - "resources": { - "privateDnsZone": { - "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" - }, - "A": { - "type": "Microsoft.Network/privateDnsZones/A", - "apiVersion": "2020-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "properties": { - "aRecords": "[parameters('aRecords')]", - "metadata": "[parameters('metadata')]", - "ttl": "[parameters('ttl')]" - } - }, - "A_roleAssignments": { - "copy": { - "name": "A_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/A/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/A', parameters('privateDnsZoneName'), 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": [ - "A" - ] - } - }, + "resources": [], "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the deployed A record." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the deployed A record." - }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/A', parameters('privateDnsZoneName'), parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group of the deployed A record." - }, - "value": "[resourceGroup().name]" + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" } } } - }, - "dependsOn": [ - "privateDnsZone" - ] + } }, - "privateDnsZone_AAAA": { - "copy": { - "name": "privateDnsZone_AAAA", - "count": "[length(coalesce(parameters('aaaa'), createArray()))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-AAAARecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "privateDnsZoneName": { - "value": "[parameters('name')]" - }, - "name": { - "value": "[coalesce(parameters('aaaa'), createArray())[copyIndex()].name]" - }, - "aaaaRecords": { - "value": "[tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'aaaaRecords')]" - }, - "metadata": { - "value": "[tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'metadata')]" - }, - "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'ttl'), 3600)]" - }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'roleAssignments')]" - } - }, - "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.32.4.45862", - "templateHash": "16709340450244912125" - }, - "name": "Private DNS Zone AAAA record", - "description": "This module deploys a Private DNS Zone AAAA record.", - "owner": "Azure/module-maintainers" - }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } - } - } - }, - "nullable": true - } - }, - "parameters": { - "privateDnsZoneName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the AAAA record." - } - }, - "aaaaRecords": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. The list of AAAA records in the record set." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata attached to the record set." - } - }, - "ttl": { - "type": "int", - "defaultValue": 3600, - "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - } - }, - "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')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", - "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" - } - }, - "resources": { - "privateDnsZone": { - "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" - }, - "AAAA": { - "type": "Microsoft.Network/privateDnsZones/AAAA", - "apiVersion": "2020-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "properties": { - "aaaaRecords": "[parameters('aaaaRecords')]", - "metadata": "[parameters('metadata')]", - "ttl": "[parameters('ttl')]" - } - }, - "AAAA_roleAssignments": { - "copy": { - "name": "AAAA_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/AAAA/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/AAAA', parameters('privateDnsZoneName'), 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": [ - "AAAA" - ] + "databaseAccount": { + "type": "Microsoft.DocumentDB/databaseAccounts", + "apiVersion": "2024-11-15", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "identity": "[variables('identity')]", + "kind": "[variables('kind')]", + "properties": "[variables('databaseAccountProperties')]" + }, + "databaseAccount_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.DocumentDB/databaseAccounts/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "databaseAccount" + ] + }, + "databaseAccount_diagnosticSettings": { + "copy": { + "name": "databaseAccount_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.DocumentDB/databaseAccounts/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null } }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the deployed AAAA record." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the deployed AAAA record." - }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/AAAA', parameters('privateDnsZoneName'), parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group of the deployed AAAA record." - }, - "value": "[resourceGroup().name]" + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" } } - } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" }, "dependsOn": [ - "privateDnsZone" + "databaseAccount" ] }, - "privateDnsZone_CNAME": { + "databaseAccount_roleAssignments": { "copy": { - "name": "privateDnsZone_CNAME", - "count": "[length(coalesce(parameters('cname'), createArray()))]" + "name": "databaseAccount_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.DocumentDB/databaseAccounts/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.DocumentDB/databaseAccounts', 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": [ + "databaseAccount" + ] + }, + "databaseAccount_sqlDatabases": { + "copy": { + "name": "databaseAccount_sqlDatabases", + "count": "[length(parameters('sqlDatabases'))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-CNAMERecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "name": "[format('{0}-sqldb-{1}', uniqueString(deployment().name, parameters('location')), parameters('sqlDatabases')[copyIndex()].name)]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "privateDnsZoneName": { - "value": "[parameters('name')]" - }, "name": { - "value": "[coalesce(parameters('cname'), createArray())[copyIndex()].name]" + "value": "[parameters('sqlDatabases')[copyIndex()].name]" }, - "cnameRecord": { - "value": "[tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'cnameRecord')]" + "containers": { + "value": "[tryGet(parameters('sqlDatabases')[copyIndex()], 'containers')]" }, - "metadata": { - "value": "[tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'metadata')]" + "throughput": { + "value": "[tryGet(parameters('sqlDatabases')[copyIndex()], 'throughput')]" }, - "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'ttl'), 3600)]" + "databaseAccountName": { + "value": "[parameters('name')]" }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'roleAssignments')]" + "autoscaleSettingsMaxThroughput": { + "value": "[tryGet(parameters('sqlDatabases')[copyIndex()], 'autoscaleSettingsMaxThroughput')]" } }, "template": { @@ -67412,184 +80145,320 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "9976020649752073181" + "version": "0.34.44.8038", + "templateHash": "16080632612286518435" }, - "name": "Private DNS Zone CNAME record", - "description": "This module deploys a Private DNS Zone CNAME record.", - "owner": "Azure/module-maintainers" - }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } - } - } - }, - "nullable": true - } + "name": "DocumentDB Database Account SQL Databases", + "description": "This module deploys a SQL Database in a CosmosDB Account." }, "parameters": { - "privateDnsZoneName": { + "databaseAccountName": { "type": "string", "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + "description": "Conditional. The name of the parent Database Account. Required if the template is used in a standalone deployment." } }, "name": { "type": "string", "metadata": { - "description": "Required. The name of the CNAME record." + "description": "Required. Name of the SQL database ." } }, - "cnameRecord": { - "type": "object", - "nullable": true, + "containers": { + "type": "array", + "items": { + "type": "object" + }, + "defaultValue": [], "metadata": { - "description": "Optional. A CNAME record." + "description": "Optional. Array of containers to deploy in the SQL database." } }, - "metadata": { - "type": "object", + "throughput": { + "type": "int", "nullable": true, "metadata": { - "description": "Optional. The metadata attached to the record set." + "description": "Optional. Request units per second. Will be ignored if autoscaleSettingsMaxThroughput is used. Setting throughput at the database level is only recommended for development/test or when workload across all containers in the shared throughput database is uniform. For best performance for large production workloads, it is recommended to set dedicated throughput (autoscale or manual) at the container level and not at the database level." } }, - "ttl": { + "autoscaleSettingsMaxThroughput": { "type": "int", - "defaultValue": 3600, + "nullable": true, "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." + "description": "Optional. Specifies the Autoscale settings and represents maximum throughput, the resource can scale up to. The autoscale throughput should have valid throughput values between 1000 and 1000000 inclusive in increments of 1000. If value is set to null, then autoscale will be disabled. Setting throughput at the database level is only recommended for development/test or when workload across all containers in the shared throughput database is uniform. For best performance for large production workloads, it is recommended to set dedicated throughput (autoscale or manual) at the container level and not at the database level." } }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", + "tags": { + "type": "object", + "nullable": true, "metadata": { - "description": "Optional. Array of role assignments to create." - } - } - }, - "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)))))]" + "description": "Optional. Tags of the SQL database resource." } - ], - "builtInRoleNames": { - "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", - "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, "resources": { - "privateDnsZone": { + "databaseAccount": { "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" + "type": "Microsoft.DocumentDB/databaseAccounts", + "apiVersion": "2024-11-15", + "name": "[parameters('databaseAccountName')]" }, - "CNAME": { - "type": "Microsoft.Network/privateDnsZones/CNAME", - "apiVersion": "2020-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "sqlDatabase": { + "type": "Microsoft.DocumentDB/databaseAccounts/sqlDatabases", + "apiVersion": "2024-11-15", + "name": "[format('{0}/{1}', parameters('databaseAccountName'), parameters('name'))]", + "tags": "[parameters('tags')]", "properties": { - "cnameRecord": "[parameters('cnameRecord')]", - "metadata": "[parameters('metadata')]", - "ttl": "[parameters('ttl')]" - } + "resource": { + "id": "[parameters('name')]" + }, + "options": "[if(contains(reference('databaseAccount').capabilities, createObject('name', 'EnableServerless')), null(), createObject('throughput', if(equals(parameters('autoscaleSettingsMaxThroughput'), null()), parameters('throughput'), null()), 'autoscaleSettings', if(not(equals(parameters('autoscaleSettingsMaxThroughput'), null())), createObject('maxThroughput', parameters('autoscaleSettingsMaxThroughput')), null())))]" + }, + "dependsOn": [ + "databaseAccount" + ] }, - "CNAME_roleAssignments": { + "container": { "copy": { - "name": "CNAME_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + "name": "container", + "count": "[length(parameters('containers'))]" }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/CNAME/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/CNAME', parameters('privateDnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-sqldb-{1}', uniqueString(deployment().name, parameters('name')), parameters('containers')[copyIndex()].name)]", "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')]" + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "databaseAccountName": { + "value": "[parameters('databaseAccountName')]" + }, + "sqlDatabaseName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[parameters('containers')[copyIndex()].name]" + }, + "analyticalStorageTtl": { + "value": "[tryGet(parameters('containers')[copyIndex()], 'analyticalStorageTtl')]" + }, + "autoscaleSettingsMaxThroughput": { + "value": "[tryGet(parameters('containers')[copyIndex()], 'autoscaleSettingsMaxThroughput')]" + }, + "conflictResolutionPolicy": { + "value": "[tryGet(parameters('containers')[copyIndex()], 'conflictResolutionPolicy')]" + }, + "defaultTtl": { + "value": "[tryGet(parameters('containers')[copyIndex()], 'defaultTtl')]" + }, + "indexingPolicy": { + "value": "[tryGet(parameters('containers')[copyIndex()], 'indexingPolicy')]" + }, + "kind": { + "value": "[tryGet(parameters('containers')[copyIndex()], 'kind')]" + }, + "version": { + "value": "[tryGet(parameters('containers')[copyIndex()], 'version')]" + }, + "paths": { + "value": "[tryGet(parameters('containers')[copyIndex()], 'paths')]" + }, + "throughput": "[if(and(or(not(equals(parameters('throughput'), null())), not(equals(parameters('autoscaleSettingsMaxThroughput'), null()))), equals(tryGet(parameters('containers')[copyIndex()], 'throughput'), null())), createObject('value', -1), createObject('value', tryGet(parameters('containers')[copyIndex()], 'throughput')))]", + "uniqueKeyPolicyKeys": { + "value": "[tryGet(parameters('containers')[copyIndex()], 'uniqueKeyPolicyKeys')]" + } + }, + "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.34.44.8038", + "templateHash": "8834615293032195419" + }, + "name": "DocumentDB Database Account SQL Database Containers", + "description": "This module deploys a SQL Database Container in a CosmosDB Account." + }, + "parameters": { + "databaseAccountName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Database Account. Required if the template is used in a standalone deployment." + } + }, + "sqlDatabaseName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent SQL Database. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the container." + } + }, + "analyticalStorageTtl": { + "type": "int", + "defaultValue": 0, + "metadata": { + "description": "Optional. Default to 0. Indicates how long data should be retained in the analytical store, for a container. Analytical store is enabled when ATTL is set with a value other than 0. If the value is set to -1, the analytical store retains all historical data, irrespective of the retention of the data in the transactional store." + } + }, + "conflictResolutionPolicy": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. The conflict resolution policy for the container. Conflicts and conflict resolution policies are applicable if the Azure Cosmos DB account is configured with multiple write regions." + } + }, + "defaultTtl": { + "type": "int", + "defaultValue": -1, + "minValue": -1, + "maxValue": 2147483647, + "metadata": { + "description": "Optional. Default to -1. Default time to live (in seconds). With Time to Live or TTL, Azure Cosmos DB provides the ability to delete items automatically from a container after a certain time period. If the value is set to \"-1\", it is equal to infinity, and items don't expire by default." + } + }, + "throughput": { + "type": "int", + "defaultValue": 400, + "metadata": { + "description": "Optional. Default to 400. Request Units per second. Will be ignored if autoscaleSettingsMaxThroughput is used. For best performance for large production workloads, it is recommended to set dedicated throughput (autoscale or manual) at the container level and not at the database level." + } + }, + "autoscaleSettingsMaxThroughput": { + "type": "int", + "nullable": true, + "maxValue": 1000000, + "metadata": { + "description": "Optional. Specifies the Autoscale settings and represents maximum throughput, the resource can scale up to. The autoscale throughput should have valid throughput values between 1000 and 1000000 inclusive in increments of 1000. If value is set to null, then autoscale will be disabled. For best performance for large production workloads, it is recommended to set dedicated throughput (autoscale or manual) at the container level and not at the database level." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the SQL Database resource." + } + }, + "paths": { + "type": "array", + "items": { + "type": "string" + }, + "minLength": 1, + "maxLength": 3, + "metadata": { + "description": "Required. List of paths using which data within the container can be partitioned. For kind=MultiHash it can be up to 3. For anything else it needs to be exactly 1." + } + }, + "indexingPolicy": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Indexing policy of the container." + } + }, + "uniqueKeyPolicyKeys": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The unique key policy configuration containing a list of unique keys that enforces uniqueness constraint on documents in the collection in the Azure Cosmos DB service." + } + }, + "kind": { + "type": "string", + "defaultValue": "Hash", + "allowedValues": [ + "Hash", + "MultiHash" + ], + "metadata": { + "description": "Optional. Default to Hash. Indicates the kind of algorithm used for partitioning." + } + }, + "version": { + "type": "int", + "defaultValue": 1, + "allowedValues": [ + 1, + 2 + ], + "metadata": { + "description": "Optional. Default to 1 for Hash and 2 for MultiHash - 1 is not allowed for MultiHash. Version of the partition key definition." + } + } + }, + "variables": { + "copy": [ + { + "name": "partitionKeyPaths", + "count": "[length(parameters('paths'))]", + "input": "[if(startsWith(parameters('paths')[copyIndex('partitionKeyPaths')], '/'), parameters('paths')[copyIndex('partitionKeyPaths')], format('/{0}', parameters('paths')[copyIndex('partitionKeyPaths')]))]" + } + ], + "containerResourceParams": "[union(createObject('conflictResolutionPolicy', parameters('conflictResolutionPolicy'), 'defaultTtl', parameters('defaultTtl'), 'id', parameters('name'), 'indexingPolicy', if(not(empty(parameters('indexingPolicy'))), parameters('indexingPolicy'), null()), 'partitionKey', createObject('paths', variables('partitionKeyPaths'), 'kind', parameters('kind'), 'version', if(equals(parameters('kind'), 'MultiHash'), 2, parameters('version'))), 'uniqueKeyPolicy', if(not(empty(parameters('uniqueKeyPolicyKeys'))), createObject('uniqueKeys', parameters('uniqueKeyPolicyKeys')), null())), if(not(equals(parameters('analyticalStorageTtl'), 0)), createObject('analyticalStorageTtl', parameters('analyticalStorageTtl')), createObject()))]" + }, + "resources": { + "databaseAccount::sqlDatabase": { + "existing": true, + "type": "Microsoft.DocumentDB/databaseAccounts/sqlDatabases", + "apiVersion": "2024-11-15", + "name": "[format('{0}/{1}', parameters('databaseAccountName'), parameters('sqlDatabaseName'))]" + }, + "databaseAccount": { + "existing": true, + "type": "Microsoft.DocumentDB/databaseAccounts", + "apiVersion": "2024-11-15", + "name": "[parameters('databaseAccountName')]" + }, + "container": { + "type": "Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers", + "apiVersion": "2024-11-15", + "name": "[format('{0}/{1}/{2}', parameters('databaseAccountName'), parameters('sqlDatabaseName'), parameters('name'))]", + "tags": "[parameters('tags')]", + "properties": { + "resource": "[variables('containerResourceParams')]", + "options": "[if(contains(reference('databaseAccount').capabilities, createObject('name', 'EnableServerless')), null(), createObject('throughput', if(and(equals(parameters('autoscaleSettingsMaxThroughput'), null()), not(equals(parameters('throughput'), -1))), parameters('throughput'), null()), 'autoscaleSettings', if(not(equals(parameters('autoscaleSettingsMaxThroughput'), null())), createObject('maxThroughput', parameters('autoscaleSettingsMaxThroughput')), null())))]" + }, + "dependsOn": [ + "databaseAccount" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the container." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the container." + }, + "value": "[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers', parameters('databaseAccountName'), parameters('sqlDatabaseName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the container was created in." + }, + "value": "[resourceGroup().name]" + } + } + } }, "dependsOn": [ - "CNAME" + "sqlDatabase" ] } }, @@ -67597,21 +80466,21 @@ "name": { "type": "string", "metadata": { - "description": "The name of the deployed CNAME record." + "description": "The name of the SQL database." }, "value": "[parameters('name')]" }, "resourceId": { "type": "string", "metadata": { - "description": "The resource ID of the deployed CNAME record." + "description": "The resource ID of the SQL database." }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/CNAME', parameters('privateDnsZoneName'), parameters('name'))]" + "value": "[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlDatabases', parameters('databaseAccountName'), parameters('name'))]" }, "resourceGroupName": { "type": "string", "metadata": { - "description": "The resource group of the deployed CNAME record." + "description": "The name of the resource group the SQL database was created in." }, "value": "[resourceGroup().name]" } @@ -67619,499 +80488,320 @@ } }, "dependsOn": [ - "privateDnsZone" + "databaseAccount" ] }, - "privateDnsZone_MX": { + "databaseAccount_sqlRoleDefinitions": { "copy": { - "name": "privateDnsZone_MX", - "count": "[length(coalesce(parameters('mx'), createArray()))]" + "name": "databaseAccount_sqlRoleDefinitions", + "count": "[length(coalesce(parameters('sqlRoleDefinitions'), createArray()))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-MXRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "name": "[format('{0}-sqlrd-{1}', uniqueString(deployment().name, parameters('location')), coalesce(parameters('sqlRoleDefinitions'), createArray())[copyIndex()].name)]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "privateDnsZoneName": { - "value": "[parameters('name')]" - }, "name": { - "value": "[coalesce(parameters('mx'), createArray())[copyIndex()].name]" + "value": "[coalesce(parameters('sqlRoleDefinitions'), createArray())[copyIndex()].name]" }, - "metadata": { - "value": "[tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'metadata')]" + "databaseAccountName": { + "value": "[parameters('name')]" }, - "mxRecords": { - "value": "[tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'mxRecords')]" + "dataActions": { + "value": "[tryGet(coalesce(parameters('sqlRoleDefinitions'), createArray())[copyIndex()], 'dataActions')]" }, - "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'ttl'), 3600)]" + "roleName": { + "value": "[tryGet(coalesce(parameters('sqlRoleDefinitions'), createArray())[copyIndex()], 'roleName')]" }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'roleAssignments')]" + "roleType": { + "value": "[tryGet(coalesce(parameters('sqlRoleDefinitions'), createArray())[copyIndex()], 'roleType')]" + }, + "principalIds": { + "value": "[parameters('sqlRoleAssignmentsPrincipalIds')]" } }, "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.32.4.45862", - "templateHash": "2520323624213076361" + "version": "0.34.44.8038", + "templateHash": "2490416937519336508" }, - "name": "Private DNS Zone MX record", - "description": "This module deploys a Private DNS Zone MX record.", - "owner": "Azure/module-maintainers" - }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } - } - } - }, - "nullable": true - } + "name": "DocumentDB Database Account SQL Role.", + "description": "This module deploys SQL Role Definision and Assignment in a CosmosDB Account." }, "parameters": { - "privateDnsZoneName": { + "databaseAccountName": { "type": "string", "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + "description": "Conditional. The name of the parent Database Account. Required if the template is used in a standalone deployment." } }, "name": { "type": "string", "metadata": { - "description": "Required. The name of the MX record." + "description": "Required. Name of the SQL Role." } }, - "metadata": { - "type": "object", - "nullable": true, + "dataActions": { + "type": "array", + "defaultValue": [ + "Microsoft.DocumentDB/databaseAccounts/readMetadata", + "Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/items/*", + "Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/*" + ], "metadata": { - "description": "Optional. The metadata attached to the record set." + "description": "Optional. An array of data actions that are allowed." } }, - "mxRecords": { + "principalIds": { "type": "array", - "nullable": true, + "defaultValue": [], "metadata": { - "description": "Optional. The list of MX records in the record set." + "description": "Optional. Ids needs to be granted." } }, - "ttl": { - "type": "int", - "defaultValue": 3600, + "roleName": { + "type": "string", + "defaultValue": "Reader Writer", "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." + "description": "Optional. A user-friendly name for the Role Definition. Must be unique for the database account." } }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", + "roleType": { + "type": "string", + "defaultValue": "CustomRole", + "allowedValues": [ + "CustomRole", + "BuiltInRole" + ], "metadata": { - "description": "Optional. Array of role assignments to create." - } - } - }, - "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)))))]" + "description": "Optional. Indicates whether the Role Definition was built-in or user created." } - ], - "builtInRoleNames": { - "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", - "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, - "resources": { - "privateDnsZone": { - "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" - }, - "MX": { - "type": "Microsoft.Network/privateDnsZones/MX", - "apiVersion": "2020-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('sql-role-definition-{0}', uniqueString(parameters('name')))]", "properties": { - "metadata": "[parameters('metadata')]", - "mxRecords": "[parameters('mxRecords')]", - "ttl": "[parameters('ttl')]" + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "databaseAccountName": { + "value": "[parameters('databaseAccountName')]" + }, + "dataActions": { + "value": "[parameters('dataActions')]" + }, + "roleName": { + "value": "[parameters('roleName')]" + }, + "roleType": { + "value": "[parameters('roleType')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.34.44.8038", + "templateHash": "16003674161646405716" + }, + "name": "DocumentDB Database Account SQL Role Definitions.", + "description": "This module deploys a SQL Role Definision in a CosmosDB Account." + }, + "parameters": { + "databaseAccountName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Database Account. Required if the template is used in a standalone deployment." + } + }, + "dataActions": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. An array of data actions that are allowed." + } + }, + "roleName": { + "type": "string", + "defaultValue": "Reader Writer", + "metadata": { + "description": "Optional. A user-friendly name for the Role Definition. Must be unique for the database account." + } + }, + "roleType": { + "type": "string", + "defaultValue": "CustomRole", + "allowedValues": [ + "CustomRole", + "BuiltInRole" + ], + "metadata": { + "description": "Optional. Indicates whether the Role Definition was built-in or user created." + } + } + }, + "resources": [ + { + "type": "Microsoft.DocumentDB/databaseAccounts/sqlRoleDefinitions", + "apiVersion": "2024-11-15", + "name": "[format('{0}/{1}', parameters('databaseAccountName'), guid(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName')), parameters('databaseAccountName'), 'sql-role'))]", + "properties": { + "assignableScopes": [ + "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName'))]" + ], + "permissions": [ + { + "dataActions": "[parameters('dataActions')]" + } + ], + "roleName": "[parameters('roleName')]", + "type": "[parameters('roleType')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the SQL database." + }, + "value": "[guid(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName')), parameters('databaseAccountName'), 'sql-role')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the SQL database." + }, + "value": "[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlRoleDefinitions', parameters('databaseAccountName'), guid(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName')), parameters('databaseAccountName'), 'sql-role'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the SQL database was created in." + }, + "value": "[resourceGroup().name]" + } + } + } } }, - "MX_roleAssignments": { + { "copy": { - "name": "MX_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + "name": "sqlRoleAssignment", + "count": "[length(parameters('principalIds'))]", + "mode": "serial", + "batchSize": 1 }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/MX/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/MX', parameters('privateDnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "condition": "[not(empty(parameters('principalIds')[copyIndex()]))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('sql-role-assign-{0}', uniqueString(parameters('principalIds')[copyIndex()]))]", "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": [ - "MX" - ] - } - }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the deployed MX record." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the deployed MX record." - }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/MX', parameters('privateDnsZoneName'), parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group of the deployed MX record." - }, - "value": "[resourceGroup().name]" - } - } - } - }, - "dependsOn": [ - "privateDnsZone" - ] - }, - "privateDnsZone_PTR": { - "copy": { - "name": "privateDnsZone_PTR", - "count": "[length(coalesce(parameters('ptr'), createArray()))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-PTRRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "privateDnsZoneName": { - "value": "[parameters('name')]" - }, - "name": { - "value": "[coalesce(parameters('ptr'), createArray())[copyIndex()].name]" - }, - "metadata": { - "value": "[tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'metadata')]" - }, - "ptrRecords": { - "value": "[tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'ptrRecords')]" - }, - "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'ttl'), 3600)]" - }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'roleAssignments')]" - } - }, - "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.32.4.45862", - "templateHash": "3080404733048745471" - }, - "name": "Private DNS Zone PTR record", - "description": "This module deploys a Private DNS Zone PTR record.", - "owner": "Azure/module-maintainers" - }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "type": "object", - "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { "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'." - } + "value": "[guid(reference(resourceId('Microsoft.Resources/deployments', format('sql-role-definition-{0}', uniqueString(parameters('name')))), '2022-09-01').outputs.resourceId.value, parameters('principalIds')[copyIndex()], resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName')))]" }, - "principalId": { - "type": "string", - "metadata": { - "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." - } + "databaseAccountName": { + "value": "[parameters('databaseAccountName')]" }, - "principalType": { - "type": "string", - "allowedValues": [ - "Device", - "ForeignGroup", - "Group", - "ServicePrincipal", - "User" - ], - "nullable": true, - "metadata": { - "description": "Optional. The principal type of the assigned principal ID." - } + "roleDefinitionId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('sql-role-definition-{0}', uniqueString(parameters('name')))), '2022-09-01').outputs.resourceId.value]" }, - "description": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The description of the role assignment." - } + "principalId": { + "value": "[parameters('principalIds')[copyIndex()]]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.34.44.8038", + "templateHash": "16164048892239373889" + }, + "name": "DocumentDB Database Account SQL Role Assignments.", + "description": "This module deploys a SQL Role Assignment in a CosmosDB Account." }, - "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\"." + "parameters": { + "databaseAccountName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Database Account. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the SQL Role Assignment." + } + }, + "principalId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Id needs to be granted." + } + }, + "roleDefinitionId": { + "type": "string", + "metadata": { + "description": "Required. Id of the SQL Role Definition." + } } }, - "conditionVersion": { - "type": "string", - "allowedValues": [ - "2.0" - ], - "nullable": true, - "metadata": { - "description": "Optional. Version of the condition." + "resources": [ + { + "type": "Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments", + "apiVersion": "2024-11-15", + "name": "[format('{0}/{1}', parameters('databaseAccountName'), parameters('name'))]", + "properties": { + "principalId": "[parameters('principalId')]", + "roleDefinitionId": "[parameters('roleDefinitionId')]", + "scope": "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName'))]" + } } - }, - "delegatedManagedIdentityResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The Resource Id of the delegated managed identity resource." + ], + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the SQL Role Assignment was created in." + }, + "value": "[resourceGroup().name]" } } } }, - "nullable": true - } - }, - "parameters": { - "privateDnsZoneName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the PTR record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata attached to the record set." - } - }, - "ptrRecords": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. The list of PTR records in the record set." - } - }, - "ttl": { - "type": "int", - "defaultValue": 3600, - "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - } - }, - "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')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", - "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" - } - }, - "resources": { - "privateDnsZone": { - "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" - }, - "PTR": { - "type": "Microsoft.Network/privateDnsZones/PTR", - "apiVersion": "2020-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "properties": { - "metadata": "[parameters('metadata')]", - "ptrRecords": "[parameters('ptrRecords')]", - "ttl": "[parameters('ttl')]" - } - }, - "PTR_roleAssignments": { - "copy": { - "name": "PTR_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/PTR/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/PTR', parameters('privateDnsZoneName'), 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": [ - "PTR" + "[resourceId('Microsoft.Resources/deployments', format('sql-role-definition-{0}', uniqueString(parameters('name'))))]" ] } - }, + ], "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the deployed PTR record." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the deployed PTR record." - }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/PTR', parameters('privateDnsZoneName'), parameters('name'))]" - }, "resourceGroupName": { "type": "string", "metadata": { - "description": "The resource group of the deployed PTR record." + "description": "The name of the resource group the SQL Role Definition and Assignment were created in." }, "value": "[resourceGroup().name]" } @@ -68119,40 +80809,37 @@ } }, "dependsOn": [ - "privateDnsZone" + "databaseAccount" ] }, - "privateDnsZone_SOA": { + "databaseAccount_mongodbDatabases": { "copy": { - "name": "privateDnsZone_SOA", - "count": "[length(coalesce(parameters('soa'), createArray()))]" + "name": "databaseAccount_mongodbDatabases", + "count": "[length(parameters('mongodbDatabases'))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-SOARecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "name": "[format('{0}-mongodb-{1}', uniqueString(deployment().name, parameters('location')), parameters('mongodbDatabases')[copyIndex()].name)]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "privateDnsZoneName": { + "databaseAccountName": { "value": "[parameters('name')]" }, "name": { - "value": "[coalesce(parameters('soa'), createArray())[copyIndex()].name]" - }, - "metadata": { - "value": "[tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'metadata')]" + "value": "[parameters('mongodbDatabases')[copyIndex()].name]" }, - "soaRecord": { - "value": "[tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'soaRecord')]" + "tags": { + "value": "[coalesce(tryGet(parameters('mongodbDatabases')[copyIndex()], 'tags'), parameters('tags'))]" }, - "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'ttl'), 3600)]" + "collections": { + "value": "[tryGet(parameters('mongodbDatabases')[copyIndex()], 'collections')]" }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'roleAssignments')]" + "throughput": { + "value": "[tryGet(parameters('mongodbDatabases')[copyIndex()], 'throughput')]" } }, "template": { @@ -68162,184 +80849,195 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "6653951445614700931" + "version": "0.34.44.8038", + "templateHash": "918699205331356852" }, - "name": "Private DNS Zone SOA record", - "description": "This module deploys a Private DNS Zone SOA record.", - "owner": "Azure/module-maintainers" - }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } - } - } - }, - "nullable": true - } + "name": "DocumentDB Database Account MongoDB Databases", + "description": "This module deploys a MongoDB Database within a CosmosDB Account." }, "parameters": { - "privateDnsZoneName": { + "databaseAccountName": { "type": "string", "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + "description": "Conditional. The name of the parent Cosmos DB database account. Required if the template is used in a standalone deployment." } }, "name": { "type": "string", "metadata": { - "description": "Required. The name of the SOA record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata attached to the record set." + "description": "Required. Name of the mongodb database." } }, - "soaRecord": { - "type": "object", - "nullable": true, + "throughput": { + "type": "int", + "defaultValue": 400, "metadata": { - "description": "Optional. A SOA record." + "description": "Optional. Request Units per second. Setting throughput at the database level is only recommended for development/test or when workload across all collections in the shared throughput database is uniform. For best performance for large production workloads, it is recommended to set dedicated throughput (autoscale or manual) at the collection level and not at the database level." } }, - "ttl": { - "type": "int", - "defaultValue": 3600, + "collections": { + "type": "array", + "defaultValue": [], "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." + "description": "Optional. Collections in the mongodb database." } }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", + "tags": { + "type": "object", + "nullable": true, "metadata": { - "description": "Optional. Array of role assignments to create." - } - } - }, - "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)))))]" + "description": "Optional. Tags of the resource." } - ], - "builtInRoleNames": { - "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", - "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, "resources": { - "privateDnsZone": { + "databaseAccount": { "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" + "type": "Microsoft.DocumentDB/databaseAccounts", + "apiVersion": "2024-11-15", + "name": "[parameters('databaseAccountName')]" }, - "SOA": { - "type": "Microsoft.Network/privateDnsZones/SOA", - "apiVersion": "2020-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "mongodbDatabase": { + "type": "Microsoft.DocumentDB/databaseAccounts/mongodbDatabases", + "apiVersion": "2024-11-15", + "name": "[format('{0}/{1}', parameters('databaseAccountName'), parameters('name'))]", + "tags": "[parameters('tags')]", "properties": { - "metadata": "[parameters('metadata')]", - "soaRecord": "[parameters('soaRecord')]", - "ttl": "[parameters('ttl')]" - } + "resource": { + "id": "[parameters('name')]" + }, + "options": "[if(contains(reference('databaseAccount').capabilities, createObject('name', 'EnableServerless')), null(), createObject('throughput', parameters('throughput')))]" + }, + "dependsOn": [ + "databaseAccount" + ] }, - "SOA_roleAssignments": { + "mongodbDatabase_collections": { "copy": { - "name": "SOA_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + "name": "mongodbDatabase_collections", + "count": "[length(parameters('collections'))]" }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/SOA/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/SOA', parameters('privateDnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-collection-{1}', uniqueString(deployment().name, parameters('name')), parameters('collections')[copyIndex()].name)]", "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')]" + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "databaseAccountName": { + "value": "[parameters('databaseAccountName')]" + }, + "mongodbDatabaseName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[parameters('collections')[copyIndex()].name]" + }, + "indexes": { + "value": "[parameters('collections')[copyIndex()].indexes]" + }, + "shardKey": { + "value": "[parameters('collections')[copyIndex()].shardKey]" + }, + "throughput": { + "value": "[tryGet(parameters('collections')[copyIndex()], 'throughput')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.34.44.8038", + "templateHash": "5747070610235343863" + }, + "name": "DocumentDB Database Account MongoDB Database Collections", + "description": "This module deploys a MongoDB Database Collection." + }, + "parameters": { + "databaseAccountName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Cosmos DB database account. Required if the template is used in a standalone deployment." + } + }, + "mongodbDatabaseName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent mongodb database. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the collection." + } + }, + "throughput": { + "type": "int", + "defaultValue": 400, + "metadata": { + "description": "Optional. Request Units per second. For best performance for large production workloads, it is recommended to set dedicated throughput (autoscale or manual) at the collection level and not at the database level." + } + }, + "indexes": { + "type": "array", + "metadata": { + "description": "Required. Indexes for the collection." + } + }, + "shardKey": { + "type": "object", + "metadata": { + "description": "Required. ShardKey for the collection." + } + } + }, + "resources": [ + { + "type": "Microsoft.DocumentDB/databaseAccounts/mongodbDatabases/collections", + "apiVersion": "2024-11-15", + "name": "[format('{0}/{1}/{2}', parameters('databaseAccountName'), parameters('mongodbDatabaseName'), parameters('name'))]", + "properties": { + "options": "[if(contains(reference(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName')), '2024-11-15').capabilities, createObject('name', 'EnableServerless')), null(), createObject('throughput', parameters('throughput')))]", + "resource": { + "id": "[parameters('name')]", + "indexes": "[parameters('indexes')]", + "shardKey": "[parameters('shardKey')]" + } + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the mongodb database collection." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the mongodb database collection." + }, + "value": "[resourceId('Microsoft.DocumentDB/databaseAccounts/mongodbDatabases/collections', parameters('databaseAccountName'), parameters('mongodbDatabaseName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the mongodb database collection was created in." + }, + "value": "[resourceGroup().name]" + } + } + } }, "dependsOn": [ - "SOA" + "mongodbDatabase" ] } }, @@ -68347,21 +81045,21 @@ "name": { "type": "string", "metadata": { - "description": "The name of the deployed SOA record." + "description": "The name of the mongodb database." }, "value": "[parameters('name')]" }, "resourceId": { "type": "string", "metadata": { - "description": "The resource ID of the deployed SOA record." + "description": "The resource ID of the mongodb database." }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/SOA', parameters('privateDnsZoneName'), parameters('name'))]" + "value": "[resourceId('Microsoft.DocumentDB/databaseAccounts/mongodbDatabases', parameters('databaseAccountName'), parameters('name'))]" }, "resourceGroupName": { "type": "string", "metadata": { - "description": "The resource group of the deployed SOA record." + "description": "The name of the resource group the mongodb database was created in." }, "value": "[resourceGroup().name]" } @@ -68369,40 +81067,40 @@ } }, "dependsOn": [ - "privateDnsZone" + "databaseAccount" ] }, - "privateDnsZone_SRV": { + "databaseAccount_gremlinDatabases": { "copy": { - "name": "privateDnsZone_SRV", - "count": "[length(coalesce(parameters('srv'), createArray()))]" + "name": "databaseAccount_gremlinDatabases", + "count": "[length(parameters('gremlinDatabases'))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-SRVRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "name": "[format('{0}-gremlin-{1}', uniqueString(deployment().name, parameters('location')), parameters('gremlinDatabases')[copyIndex()].name)]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "privateDnsZoneName": { + "databaseAccountName": { "value": "[parameters('name')]" }, "name": { - "value": "[coalesce(parameters('srv'), createArray())[copyIndex()].name]" - }, - "metadata": { - "value": "[tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'metadata')]" + "value": "[parameters('gremlinDatabases')[copyIndex()].name]" }, - "srvRecords": { - "value": "[tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'srvRecords')]" + "tags": { + "value": "[coalesce(tryGet(parameters('gremlinDatabases')[copyIndex()], 'tags'), parameters('tags'))]" }, - "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'ttl'), 3600)]" + "graphs": { + "value": "[tryGet(parameters('gremlinDatabases')[copyIndex()], 'graphs')]" }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'roleAssignments')]" + "maxThroughput": { + "value": "[tryGet(parameters('gremlinDatabases')[copyIndex()], 'maxThroughput')]" + }, + "throughput": { + "value": "[tryGet(parameters('gremlinDatabases')[copyIndex()], 'throughput')]" } }, "template": { @@ -68412,184 +81110,214 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "5790774778713328446" + "version": "0.34.44.8038", + "templateHash": "3102415923148662010" }, - "name": "Private DNS Zone SRV record", - "description": "This module deploys a Private DNS Zone SRV record.", - "owner": "Azure/module-maintainers" - }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } - } - } - }, - "nullable": true - } + "name": "DocumentDB Database Account Gremlin Databases", + "description": "This module deploys a Gremlin Database within a CosmosDB Account." }, "parameters": { - "privateDnsZoneName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." - } - }, "name": { "type": "string", "metadata": { - "description": "Required. The name of the SRV record." + "description": "Required. Name of the Gremlin database." } }, - "metadata": { + "tags": { "type": "object", "nullable": true, "metadata": { - "description": "Optional. The metadata attached to the record set." + "description": "Optional. Tags of the Gremlin database resource." } }, - "srvRecords": { + "databaseAccountName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Gremlin database. Required if the template is used in a standalone deployment." + } + }, + "graphs": { "type": "array", - "nullable": true, + "defaultValue": [], "metadata": { - "description": "Optional. The list of SRV records in the record set." + "description": "Optional. Array of graphs to deploy in the Gremlin database." } }, - "ttl": { + "maxThroughput": { "type": "int", - "defaultValue": 3600, + "defaultValue": 4000, "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." + "description": "Optional. Represents maximum throughput, the resource can scale up to. Cannot be set together with `throughput`. If `throughput` is set to something else than -1, this autoscale setting is ignored. Setting throughput at the database level is only recommended for development/test or when workload across all graphs in the shared throughput database is uniform. For best performance for large production workloads, it is recommended to set dedicated throughput (autoscale or manual) at the graph level and not at the database level." } }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", + "throughput": { + "type": "int", + "nullable": true, "metadata": { - "description": "Optional. Array of role assignments to create." - } - } - }, - "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)))))]" + "description": "Optional. Request Units per second (for example 10000). Cannot be set together with `maxThroughput`. Setting throughput at the database level is only recommended for development/test or when workload across all graphs in the shared throughput database is uniform. For best performance for large production workloads, it is recommended to set dedicated throughput (autoscale or manual) at the graph level and not at the database level." } - ], - "builtInRoleNames": { - "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", - "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, "resources": { - "privateDnsZone": { + "databaseAccount": { "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" + "type": "Microsoft.DocumentDB/databaseAccounts", + "apiVersion": "2024-11-15", + "name": "[parameters('databaseAccountName')]" }, - "SRV": { - "type": "Microsoft.Network/privateDnsZones/SRV", - "apiVersion": "2020-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "gremlinDatabase": { + "type": "Microsoft.DocumentDB/databaseAccounts/gremlinDatabases", + "apiVersion": "2024-11-15", + "name": "[format('{0}/{1}', parameters('databaseAccountName'), parameters('name'))]", + "tags": "[parameters('tags')]", "properties": { - "metadata": "[parameters('metadata')]", - "srvRecords": "[parameters('srvRecords')]", - "ttl": "[parameters('ttl')]" - } + "options": "[if(contains(reference('databaseAccount').capabilities, createObject('name', 'EnableServerless')), createObject(), createObject('autoscaleSettings', if(equals(parameters('throughput'), null()), createObject('maxThroughput', parameters('maxThroughput')), null()), 'throughput', parameters('throughput')))]", + "resource": { + "id": "[parameters('name')]" + } + }, + "dependsOn": [ + "databaseAccount" + ] }, - "SRV_roleAssignments": { + "gremlinDatabase_gremlinGraphs": { "copy": { - "name": "SRV_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + "name": "gremlinDatabase_gremlinGraphs", + "count": "[length(parameters('graphs'))]" }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/SRV/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/SRV', parameters('privateDnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-gremlindb-{1}', uniqueString(deployment().name, parameters('name')), parameters('graphs')[copyIndex()].name)]", "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')]" + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('graphs')[copyIndex()].name]" + }, + "gremlinDatabaseName": { + "value": "[parameters('name')]" + }, + "databaseAccountName": { + "value": "[parameters('databaseAccountName')]" + }, + "indexingPolicy": { + "value": "[tryGet(parameters('graphs')[copyIndex()], 'indexingPolicy')]" + }, + "partitionKeyPaths": "[if(not(empty(parameters('graphs')[copyIndex()].partitionKeyPaths)), createObject('value', parameters('graphs')[copyIndex()].partitionKeyPaths), createObject('value', createArray()))]" + }, + "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.34.44.8038", + "templateHash": "14448207336426896249" + }, + "name": "DocumentDB Database Accounts Gremlin Databases Graphs", + "description": "This module deploys a DocumentDB Database Accounts Gremlin Database Graph." + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the graph." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the Gremlin graph resource." + } + }, + "databaseAccountName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Database Account. Required if the template is used in a standalone deployment." + } + }, + "gremlinDatabaseName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Gremlin Database. Required if the template is used in a standalone deployment." + } + }, + "indexingPolicy": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Indexing policy of the graph." + } + }, + "partitionKeyPaths": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. List of paths using which data within the container can be partitioned." + } + } + }, + "resources": { + "databaseAccount::gremlinDatabase": { + "existing": true, + "type": "Microsoft.DocumentDB/databaseAccounts/gremlinDatabases", + "apiVersion": "2024-11-15", + "name": "[format('{0}/{1}', parameters('databaseAccountName'), parameters('gremlinDatabaseName'))]" + }, + "databaseAccount": { + "existing": true, + "type": "Microsoft.DocumentDB/databaseAccounts", + "apiVersion": "2024-11-15", + "name": "[parameters('databaseAccountName')]" + }, + "gremlinGraph": { + "type": "Microsoft.DocumentDB/databaseAccounts/gremlinDatabases/graphs", + "apiVersion": "2024-11-15", + "name": "[format('{0}/{1}/{2}', parameters('databaseAccountName'), parameters('gremlinDatabaseName'), parameters('name'))]", + "tags": "[parameters('tags')]", + "properties": { + "resource": { + "id": "[parameters('name')]", + "indexingPolicy": "[if(not(empty(parameters('indexingPolicy'))), parameters('indexingPolicy'), null())]", + "partitionKey": { + "paths": "[if(not(empty(parameters('partitionKeyPaths'))), parameters('partitionKeyPaths'), null())]" + } + } + } + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the graph." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the graph." + }, + "value": "[resourceId('Microsoft.DocumentDB/databaseAccounts/gremlinDatabases/graphs', parameters('databaseAccountName'), parameters('gremlinDatabaseName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the graph was created in." + }, + "value": "[resourceGroup().name]" + } + } + } }, "dependsOn": [ - "SRV" + "gremlinDatabase" ] } }, @@ -68597,21 +81325,21 @@ "name": { "type": "string", "metadata": { - "description": "The name of the deployed SRV record." + "description": "The name of the Gremlin database." }, "value": "[parameters('name')]" }, "resourceId": { "type": "string", "metadata": { - "description": "The resource ID of the deployed SRV record." + "description": "The resource ID of the Gremlin database." }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/SRV', parameters('privateDnsZoneName'), parameters('name'))]" + "value": "[resourceId('Microsoft.DocumentDB/databaseAccounts/gremlinDatabases', parameters('databaseAccountName'), parameters('name'))]" }, "resourceGroupName": { "type": "string", "metadata": { - "description": "The resource group of the deployed SRV record." + "description": "The name of the resource group the Gremlin database was created in." }, "value": "[resourceGroup().name]" } @@ -68619,40 +81347,37 @@ } }, "dependsOn": [ - "privateDnsZone" + "databaseAccount" ] }, - "privateDnsZone_TXT": { + "databaseAccount_tables": { "copy": { - "name": "privateDnsZone_TXT", - "count": "[length(coalesce(parameters('txt'), createArray()))]" + "name": "databaseAccount_tables", + "count": "[length(parameters('tables'))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-TXTRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "name": "[format('{0}-table-{1}', uniqueString(deployment().name, parameters('location')), parameters('tables')[copyIndex()].name)]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "privateDnsZoneName": { + "databaseAccountName": { "value": "[parameters('name')]" }, "name": { - "value": "[coalesce(parameters('txt'), createArray())[copyIndex()].name]" - }, - "metadata": { - "value": "[tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'metadata')]" + "value": "[parameters('tables')[copyIndex()].name]" }, - "txtRecords": { - "value": "[tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'txtRecords')]" + "tags": { + "value": "[coalesce(tryGet(parameters('tables')[copyIndex()], 'tags'), parameters('tags'))]" }, - "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'ttl'), 3600)]" + "maxThroughput": { + "value": "[tryGet(parameters('tables')[copyIndex()], 'maxThroughput')]" }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'roleAssignments')]" + "throughput": { + "value": "[tryGet(parameters('tables')[copyIndex()], 'throughput')]" } }, "template": { @@ -68662,184 +81387,67 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "1855369119498044639" + "version": "0.34.44.8038", + "templateHash": "6386293577244138652" }, - "name": "Private DNS Zone TXT record", - "description": "This module deploys a Private DNS Zone TXT record.", - "owner": "Azure/module-maintainers" - }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } - } - } - }, - "nullable": true - } + "name": "Azure Cosmos DB account tables", + "description": "This module deploys a table within an Azure Cosmos DB Account." }, "parameters": { - "privateDnsZoneName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." - } - }, "name": { "type": "string", "metadata": { - "description": "Required. The name of the TXT record." + "description": "Required. Name of the table." } }, - "metadata": { + "tags": { "type": "object", "nullable": true, "metadata": { - "description": "Optional. The metadata attached to the record set." - } - }, - "ttl": { - "type": "int", - "defaultValue": 3600, - "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." + "description": "Optional. Tags for the table." } }, - "txtRecords": { - "type": "array", - "nullable": true, + "databaseAccountName": { + "type": "string", "metadata": { - "description": "Optional. The list of TXT records in the record set." + "description": "Conditional. The name of the parent Azure Cosmos DB account. Required if the template is used in a standalone deployment." } }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", + "maxThroughput": { + "type": "int", + "defaultValue": 4000, "metadata": { - "description": "Optional. Array of role assignments to create." - } - } - }, - "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)))))]" + "description": "Optional. Represents maximum throughput, the resource can scale up to. Cannot be set together with `throughput`. If `throughput` is set to something else than -1, this autoscale setting is ignored." } - ], - "builtInRoleNames": { - "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", - "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" - } - }, - "resources": { - "privateDnsZone": { - "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" }, - "TXT": { - "type": "Microsoft.Network/privateDnsZones/TXT", - "apiVersion": "2020-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "properties": { - "metadata": "[parameters('metadata')]", - "ttl": "[parameters('ttl')]", - "txtRecords": "[parameters('txtRecords')]" + "throughput": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Request Units per second (for example 10000). Cannot be set together with `maxThroughput`." } + } + }, + "resources": { + "databaseAccount": { + "existing": true, + "type": "Microsoft.DocumentDB/databaseAccounts", + "apiVersion": "2024-11-15", + "name": "[parameters('databaseAccountName')]" }, - "TXT_roleAssignments": { - "copy": { - "name": "TXT_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/TXT/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/TXT', parameters('privateDnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "table": { + "type": "Microsoft.DocumentDB/databaseAccounts/tables", + "apiVersion": "2024-11-15", + "name": "[format('{0}/{1}', parameters('databaseAccountName'), parameters('name'))]", + "tags": "[parameters('tags')]", "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')]" + "options": "[if(contains(reference('databaseAccount').capabilities, createObject('name', 'EnableServerless')), createObject(), createObject('autoscaleSettings', if(equals(parameters('throughput'), null()), createObject('maxThroughput', parameters('maxThroughput')), null()), 'throughput', parameters('throughput')))]", + "resource": { + "id": "[parameters('name')]" + } }, "dependsOn": [ - "TXT" + "databaseAccount" ] } }, @@ -68847,21 +81455,21 @@ "name": { "type": "string", "metadata": { - "description": "The name of the deployed TXT record." + "description": "The name of the table." }, "value": "[parameters('name')]" }, "resourceId": { "type": "string", "metadata": { - "description": "The resource ID of the deployed TXT record." + "description": "The resource ID of the table." }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/TXT', parameters('privateDnsZoneName'), parameters('name'))]" + "value": "[resourceId('Microsoft.DocumentDB/databaseAccounts/tables', parameters('databaseAccountName'), parameters('name'))]" }, "resourceGroupName": { "type": "string", "metadata": { - "description": "The resource group of the deployed TXT record." + "description": "The name of the resource group the table was created in." }, "value": "[resourceGroup().name]" } @@ -68869,43 +81477,62 @@ } }, "dependsOn": [ - "privateDnsZone" + "databaseAccount" ] }, - "privateDnsZone_virtualNetworkLinks": { + "databaseAccount_privateEndpoints": { "copy": { - "name": "privateDnsZone_virtualNetworkLinks", - "count": "[length(coalesce(parameters('virtualNetworkLinks'), createArray()))]" + "name": "databaseAccount_privateEndpoints", + "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-VirtualNetworkLink-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "name": "[format('{0}-dbAccount-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]]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "privateDnsZoneName": { - "value": "[parameters('name')]" - }, "name": { - "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'name'), format('{0}-vnetlink', last(split(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()].virtualNetworkResourceId, '/'))))]" + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'name'), format('pep-{0}-{1}-{2}', last(split(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '/')), coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service, copyIndex()))]" }, - "virtualNetworkResourceId": { - "value": "[coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()].virtualNetworkResourceId]" + "privateLinkServiceConnections": "[if(not(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true())), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '/')), coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service, copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), 'groupIds', createArray(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service))))), createObject('value', null()))]", + "manualPrivateLinkServiceConnections": "[if(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true()), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '/')), coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service, copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), 'groupIds', createArray(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service), 'requestMessage', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualConnectionRequestMessage'), 'Manual approval required.'))))), createObject('value', null()))]", + "subnetResourceId": { + "value": "[coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId]" + }, + "enableTelemetry": { + "value": "[variables('enableReferencedModulesTelemetry')]" }, "location": { - "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'location'), 'global')]" + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'location'), reference(split(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location)]" }, - "registrationEnabled": { - "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'registrationEnabled'), false())]" + "lock": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" + }, + "privateDnsZoneGroup": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroup')]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'roleAssignments')]" }, "tags": { - "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" }, - "resolutionPolicy": { - "value": "[tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'resolutionPolicy')]" + "customDnsConfigs": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customDnsConfigs')]" + }, + "ipConfigurations": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'ipConfigurations')]" + }, + "applicationSecurityGroupResourceIds": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'applicationSecurityGroupResourceIds')]" + }, + "customNetworkInterfaceName": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customNetworkInterfaceName')]" } }, "template": { @@ -68915,967 +81542,1562 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "15326596012552051215" + "version": "0.33.13.18514", + "templateHash": "15954548978129725136" }, - "name": "Private DNS Zone Virtual Network Link", - "description": "This module deploys a Private DNS Zone Virtual Network Link.", - "owner": "Azure/module-maintainers" + "name": "Private Endpoints", + "description": "This module deploys a Private Endpoint." + }, + "definitions": { + "privateDnsZoneGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, + "metadata": { + "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "ipConfigurationType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource that is unique within a resource group." + } + }, + "properties": { + "type": "object", + "properties": { + "groupId": { + "type": "string", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } + } + }, + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "privateLinkServiceConnectionType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the private link service connection." + } + }, + "properties": { + "type": "object", + "properties": { + "groupIds": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." + } + }, + "privateLinkServiceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of private link service." + } + }, + "requestMessage": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." + } + } + }, + "metadata": { + "description": "Required. Properties of private link service connection." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "customDnsConfigType": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. FQDN that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "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." + } + } + }, + "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.5.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": { + "__bicep_imported_from!": { + "sourceTemplate": "private-dns-zone-group/main.bicep" + } + } + }, + "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.5.1" + } + } + } }, "parameters": { - "privateDnsZoneName": { + "name": { "type": "string", "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + "description": "Required. Name of the private endpoint resource to create." } }, - "name": { + "subnetResourceId": { "type": "string", - "defaultValue": "[format('{0}-vnetlink', last(split(parameters('virtualNetworkResourceId'), '/')))]", "metadata": { - "description": "Optional. The name of the virtual network link." + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + } + }, + "applicationSecurityGroupResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Application security groups in which the private endpoint IP configuration is included." + } + }, + "customNetworkInterfaceName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The custom name of the network interface attached to the private endpoint." + } + }, + "ipConfigurations": { + "type": "array", + "items": { + "$ref": "#/definitions/ipConfigurationType" + }, + "nullable": true, + "metadata": { + "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." + } + }, + "privateDnsZoneGroup": { + "$ref": "#/definitions/privateDnsZoneGroupType", + "nullable": true, + "metadata": { + "description": "Optional. The private DNS zone group to configure for the private endpoint." } }, "location": { "type": "string", - "defaultValue": "global", + "defaultValue": "[resourceGroup().location]", "metadata": { - "description": "Optional. The location of the PrivateDNSZone. Should be global." + "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", "nullable": true, "metadata": { - "description": "Optional. Tags of the resource." + "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." } }, - "registrationEnabled": { - "type": "bool", - "defaultValue": false, + "customDnsConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/customDnsConfigType" + }, + "nullable": true, "metadata": { - "description": "Optional. Is auto-registration of virtual machine records in the virtual network in the Private DNS zone enabled?." + "description": "Optional. Custom DNS configurations." } }, - "virtualNetworkResourceId": { - "type": "string", + "manualPrivateLinkServiceConnections": { + "type": "array", + "items": { + "$ref": "#/definitions/privateLinkServiceConnectionType" + }, + "nullable": true, "metadata": { - "description": "Required. Link to another virtual network resource ID." + "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." } }, - "resolutionPolicy": { - "type": "string", + "privateLinkServiceConnections": { + "type": "array", + "items": { + "$ref": "#/definitions/privateLinkServiceConnectionType" + }, "nullable": true, "metadata": { - "description": "Optional. The resolution policy on the virtual network link. Only applicable for virtual network links to privatelink zones, and for A,AAAA,CNAME queries. When set to `NxDomainRedirect`, Azure DNS resolver falls back to public resolution if private dns query resolution results in non-existent domain response. `Default` is configured as the default option." + "description": "Conditional. A grouping of information about the connection to the remote resource. Required if `manualPrivateLinkServiceConnections` is empty." + } + }, + "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": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.10.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" + } + } + } } - } - }, - "resources": { - "privateDnsZone": { - "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" }, - "virtualNetworkLink": { - "type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks", - "apiVersion": "2024-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "privateEndpoint": { + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-11-01", + "name": "[parameters('name')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", "properties": { - "registrationEnabled": "[parameters('registrationEnabled')]", - "virtualNetwork": { - "id": "[parameters('virtualNetworkResourceId')]" - }, - "resolutionPolicy": "[parameters('resolutionPolicy')]" + "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": "[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": "2022-09-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.33.13.18514", + "templateHash": "5440815542537978381" + }, + "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 + } + } + }, + "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." + } + } + }, + "variables": { + "copy": [ + { + "name": "privateDnsZoneConfigsVar", + "count": "[length(parameters('privateDnsZoneConfigs'))]", + "input": { + "name": "[coalesce(tryGet(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')], 'name'), last(split(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId, '/')))]", + "properties": { + "privateDnsZoneId": "[parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId]" + } + } + } + ] + }, + "resources": { + "privateEndpoint": { + "existing": true, + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-11-01", + "name": "[parameters('privateEndpointName')]" + }, + "privateDnsZoneGroup": { + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2023-11-01", + "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", + "properties": { + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" + } + } + }, + "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": { - "name": { + "resourceGroupName": { "type": "string", "metadata": { - "description": "The name of the deployed virtual network link." + "description": "The resource group the private endpoint was deployed into." }, - "value": "[parameters('name')]" + "value": "[resourceGroup().name]" }, "resourceId": { "type": "string", "metadata": { - "description": "The resource ID of the deployed virtual network link." + "description": "The resource ID of the private endpoint." }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/virtualNetworkLinks', parameters('privateDnsZoneName'), parameters('name'))]" + "value": "[resourceId('Microsoft.Network/privateEndpoints', parameters('name'))]" }, - "resourceGroupName": { + "name": { "type": "string", "metadata": { - "description": "The resource group of the deployed virtual network link." + "description": "The name of the private endpoint." }, - "value": "[resourceGroup().name]" + "value": "[parameters('name')]" }, "location": { "type": "string", "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('virtualNetworkLink', '2024-06-01', 'full').location]" - } - } - } - }, - "dependsOn": [ - "privateDnsZone" - ] - } - }, - "outputs": { - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group the private DNS zone was deployed into." - }, - "value": "[resourceGroup().name]" - }, - "name": { - "type": "string", - "metadata": { - "description": "The name of the private DNS zone." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the private DNS zone." - }, - "value": "[resourceId('Microsoft.Network/privateDnsZones', parameters('name'))]" - }, - "location": { - "type": "string", - "metadata": { - "description": "The location the resource was deployed into." - }, - "value": "[reference('privateDnsZone', '2020-06-01', 'full').location]" - } - } - } - } - }, - { - "condition": "[parameters('networkIsolation')]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[take(format('{0}-apim-private-endpoint-deployment', parameters('name')), 64)]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[toLower(format('pep-{0}', reference(resourceId('Microsoft.Resources/deployments', take(format('{0}-apim-deployment', parameters('name')), 64)), '2022-09-01').outputs.name.value))]" - }, - "subnetResourceId": { - "value": "[parameters('virtualNetworkSubnetResourceId')]" - }, - "privateDnsZoneGroup": { - "value": { - "privateDnsZoneGroupConfigs": [ - { - "privateDnsZoneResourceId": "[reference(resourceId('Microsoft.Resources/deployments', 'private-dns-apim-deployment'), '2022-09-01').outputs.resourceId.value]" - } - ] - } - }, - "privateLinkServiceConnections": { - "value": [ - { - "name": "[reference(resourceId('Microsoft.Resources/deployments', take(format('{0}-apim-deployment', parameters('name')), 64)), '2022-09-01').outputs.name.value]", - "properties": { - "groupIds": [ - "Gateway" - ], - "privateLinkServiceId": "[reference(resourceId('Microsoft.Resources/deployments', take(format('{0}-apim-deployment', parameters('name')), 64)), '2022-09-01').outputs.resourceId.value]" - } - } - ] - } - }, - "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.34.44.8038", - "templateHash": "12389807800450456797" - }, - "name": "Private Endpoints", - "description": "This module deploys a Private Endpoint." - }, - "definitions": { - "privateDnsZoneGroupType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the Private DNS Zone Group." - } - }, - "privateDnsZoneGroupConfigs": { - "type": "array", - "items": { - "$ref": "#/definitions/privateDnsZoneGroupConfigType" - }, - "metadata": { - "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." - } - } - }, - "metadata": { - "__bicep_export!": true - } - }, - "ipConfigurationType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the resource that is unique within a resource group." - } - }, - "properties": { - "type": "object", - "properties": { - "groupId": { - "type": "string", - "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." - } + "value": "[reference('privateEndpoint', '2023-11-01', 'full').location]" }, - "memberName": { - "type": "string", + "customDnsConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/customDnsConfigType" + }, "metadata": { - "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." - } + "description": "The custom DNS configurations of the private endpoint." + }, + "value": "[reference('privateEndpoint').customDnsConfigs]" }, - "privateIPAddress": { - "type": "string", - "metadata": { - "description": "Required. A private IP address obtained from the private endpoint's subnet." - } - } - }, - "metadata": { - "description": "Required. Properties of private endpoint IP configurations." - } - } - }, - "metadata": { - "__bicep_export!": true - } - }, - "privateLinkServiceConnectionType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the private link service connection." - } - }, - "properties": { - "type": "object", - "properties": { - "groupIds": { + "networkInterfaceResourceIds": { "type": "array", "items": { "type": "string" }, "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." - } - }, - "privateLinkServiceId": { - "type": "string", - "metadata": { - "description": "Required. The resource id of private link service." - } + "description": "The resource IDs of the network interfaces associated with the private endpoint." + }, + "value": "[map(reference('privateEndpoint').networkInterfaces, lambda('nic', lambdaVariables('nic').id))]" }, - "requestMessage": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." - } - } - }, - "metadata": { - "description": "Required. Properties of private link service connection." - } - } - }, - "metadata": { - "__bicep_export!": true - } - }, - "customDnsConfigType": { - "type": "object", - "properties": { - "fqdn": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. FQDN that resolves to private endpoint IP address." - } - }, - "ipAddresses": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. A list of private IP addresses of the private endpoint." - } - } - }, - "metadata": { - "__bicep_export!": true - } - }, - "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." - } - } - }, - "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.5.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." + "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))]" + } } } }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "private-dns-zone-group/main.bicep" - } - } + "dependsOn": [ + "databaseAccount" + ] }, - "roleAssignmentType": { - "type": "object", + "secretsExport": { + "condition": "[not(equals(parameters('secretsExportConfiguration'), null()))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-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]]", "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\"." - } + "expressionEvaluationOptions": { + "scope": "inner" }, - "conditionVersion": { - "type": "string", - "allowedValues": [ - "2.0" - ], - "nullable": true, - "metadata": { - "description": "Optional. Version of the condition." + "mode": "Incremental", + "parameters": { + "keyVaultName": { + "value": "[last(split(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '/'))]" + }, + "secretsToSet": { + "value": "[union(createArray(), if(contains(parameters('secretsExportConfiguration'), 'primaryWriteKeySecretName'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'primaryWriteKeySecretName'), 'value', listKeys(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '2024-11-15').primaryMasterKey)), createArray()), if(contains(parameters('secretsExportConfiguration'), 'primaryReadOnlyKeySecretName'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'primaryReadOnlyKeySecretName'), 'value', listKeys(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '2024-11-15').primaryReadonlyMasterKey)), createArray()), if(contains(parameters('secretsExportConfiguration'), 'primaryWriteConnectionStringSecretName'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'primaryWriteConnectionStringSecretName'), 'value', listConnectionStrings(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '2024-11-15').connectionStrings[0].connectionString)), createArray()), if(contains(parameters('secretsExportConfiguration'), 'primaryReadonlyConnectionStringSecretName'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'primaryReadonlyConnectionStringSecretName'), 'value', listConnectionStrings(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '2024-11-15').connectionStrings[2].connectionString)), createArray()), if(contains(parameters('secretsExportConfiguration'), 'secondaryWriteKeySecretName'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'secondaryWriteKeySecretName'), 'value', listKeys(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '2024-11-15').secondaryMasterKey)), createArray()), if(contains(parameters('secretsExportConfiguration'), 'secondaryReadonlyKeySecretName'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'secondaryReadonlyKeySecretName'), 'value', listKeys(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '2024-11-15').secondaryReadonlyMasterKey)), createArray()), if(contains(parameters('secretsExportConfiguration'), 'secondaryWriteConnectionStringSecretName'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'secondaryWriteConnectionStringSecretName'), 'value', listConnectionStrings(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '2024-11-15').connectionStrings[1].connectionString)), createArray()), if(contains(parameters('secretsExportConfiguration'), 'secondaryReadonlyConnectionStringSecretName'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'secondaryReadonlyConnectionStringSecretName'), 'value', listConnectionStrings(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '2024-11-15').connectionStrings[3].connectionString)), createArray()))]" } }, - "delegatedManagedIdentityResourceId": { - "type": "string", - "nullable": true, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", "metadata": { - "description": "Optional. The Resource Id of the delegated managed identity resource." + "_generator": { + "name": "bicep", + "version": "0.34.44.8038", + "templateHash": "17295277467511711636" + } + }, + "definitions": { + "secretSetType": { + "type": "object", + "properties": { + "secretResourceId": { + "type": "string", + "metadata": { + "description": "The resourceId of the exported secret." + } + }, + "secretUri": { + "type": "string", + "metadata": { + "description": "The secret URI of the exported secret." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for the secret set." + } + }, + "secretToSetType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the secret to set." + } + }, + "value": { + "type": "securestring", + "metadata": { + "description": "Required. The value of the secret to set." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for the secrets to set." + } + } + }, + "parameters": { + "keyVaultName": { + "type": "string", + "metadata": { + "description": "Required. The name of the Key Vault to set the ecrets in." + } + }, + "secretsToSet": { + "type": "array", + "items": { + "$ref": "#/definitions/secretToSetType" + }, + "metadata": { + "description": "Required. The secrets to set in the Key Vault." + } + } + }, + "resources": { + "keyVault": { + "existing": true, + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2023-07-01", + "name": "[parameters('keyVaultName')]" + }, + "secrets": { + "copy": { + "name": "secrets", + "count": "[length(parameters('secretsToSet'))]" + }, + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2023-07-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('secretsToSet')[copyIndex()].name)]", + "properties": { + "value": "[parameters('secretsToSet')[copyIndex()].value]" + } + } + }, + "outputs": { + "secretsSet": { + "type": "array", + "items": { + "$ref": "#/definitions/secretSetType" + }, + "metadata": { + "description": "The references to the secrets exported to the provided Key Vault." + }, + "copy": { + "count": "[length(range(0, length(coalesce(parameters('secretsToSet'), createArray()))))]", + "input": { + "secretResourceId": "[resourceId('Microsoft.KeyVault/vaults/secrets', parameters('keyVaultName'), parameters('secretsToSet')[range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()]].name)]", + "secretUri": "[reference(format('secrets[{0}]', range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()])).secretUri]" + } + } + } } } }, - "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.5.1" - } - } + "dependsOn": [ + "databaseAccount" + ] } }, - "parameters": { + "outputs": { + "exportedSecrets": { + "$ref": "#/definitions/secretsOutputType", + "metadata": { + "description": "The references to the secrets exported to the provided Key Vault." + }, + "value": "[if(not(equals(parameters('secretsExportConfiguration'), null())), toObject(reference('secretsExport').outputs.secretsSet.value, lambda('secret', last(split(lambdaVariables('secret').secretResourceId, '/'))), lambda('secret', lambdaVariables('secret'))), createObject())]" + }, "name": { "type": "string", "metadata": { - "description": "Required. Name of the private endpoint resource to create." - } + "description": "The name of the database account." + }, + "value": "[parameters('name')]" }, - "subnetResourceId": { + "resourceId": { "type": "string", "metadata": { - "description": "Required. Resource ID of the subnet where the endpoint needs to be created." - } - }, - "applicationSecurityGroupResourceIds": { - "type": "array", - "items": { - "type": "string" + "description": "The resource ID of the database account." }, - "nullable": true, - "metadata": { - "description": "Optional. Application security groups in which the private endpoint IP configuration is included." - } + "value": "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name'))]" }, - "customNetworkInterfaceName": { + "resourceGroupName": { "type": "string", - "nullable": true, "metadata": { - "description": "Optional. The custom name of the network interface attached to the private endpoint." - } - }, - "ipConfigurations": { - "type": "array", - "items": { - "$ref": "#/definitions/ipConfigurationType" + "description": "The name of the resource group the database account was created in." }, - "nullable": true, - "metadata": { - "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." - } + "value": "[resourceGroup().name]" }, - "privateDnsZoneGroup": { - "$ref": "#/definitions/privateDnsZoneGroupType", + "systemAssignedMIPrincipalId": { + "type": "string", "nullable": true, "metadata": { - "description": "Optional. The private DNS zone group to configure for the private endpoint." - } + "description": "The principal ID of the system assigned identity." + }, + "value": "[tryGet(tryGet(reference('databaseAccount', '2024-11-15', 'full'), 'identity'), 'principalId')]" }, "location": { "type": "string", - "defaultValue": "[resourceGroup().location]", - "metadata": { - "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" + "description": "The location the resource was deployed into." }, - "nullable": true, - "metadata": { - "description": "Optional. Array of role assignments to create." - } + "value": "[reference('databaseAccount', '2024-11-15', 'full').location]" }, - "tags": { - "type": "object", - "nullable": true, + "endpoint": { + "type": "string", "metadata": { - "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." - } - }, - "customDnsConfigs": { - "type": "array", - "items": { - "$ref": "#/definitions/customDnsConfigType" + "description": "The endpoint of the database account." }, - "nullable": true, - "metadata": { - "description": "Optional. Custom DNS configurations." - } + "value": "[reference('databaseAccount').documentEndpoint]" }, - "manualPrivateLinkServiceConnections": { + "privateEndpoints": { "type": "array", "items": { - "$ref": "#/definitions/privateLinkServiceConnectionType" + "$ref": "#/definitions/privateEndpointOutputType" }, - "nullable": true, "metadata": { - "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." - } - }, - "privateLinkServiceConnections": { - "type": "array", - "items": { - "$ref": "#/definitions/privateLinkServiceConnectionType" + "description": "The private endpoints of the database account." }, - "nullable": true, - "metadata": { - "description": "Conditional. A grouping of information about the connection to the remote resource. Required if `manualPrivateLinkServiceConnections` is empty." - } - }, - "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": "2024-03-01", - "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.11.0', '.', '-'), 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" - } - } + "copy": { + "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]", + "input": { + "name": "[reference(format('databaseAccount_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", + "resourceId": "[reference(format('databaseAccount_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", + "groupId": "[tryGet(tryGet(reference(format('databaseAccount_privateEndpoints[{0}]', copyIndex())).outputs, 'groupId'), 'value')]", + "customDnsConfigs": "[reference(format('databaseAccount_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfigs.value]", + "networkInterfaceResourceIds": "[reference(format('databaseAccount_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceResourceIds.value]" } } - }, - "privateEndpoint": { - "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2024-05-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')]" + } + } + } + }, + "dependsOn": [ + "privateDnsZone" + ] + } + }, + "outputs": { + "resourceId": { + "type": "string", + "value": "[reference('cosmosDb').outputs.resourceId.value]" + }, + "cosmosDBname": { + "type": "string", + "value": "[reference('cosmosDb').outputs.name.value]" + } + } + } + }, + "dependsOn": [ + "appIdentity", + "logAnalyticsWorkspace", + "network" + ] + }, + "sqlServer": { + "condition": "[parameters('sqlServerEnabled')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('{0}-sqlserver-deployment', parameters('name')), 64)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[format('sql{0}{1}', parameters('name'), variables('resourceToken'))]" + }, + "administratorLogin": { + "value": "[variables('servicesUsername')]" + }, + "administratorLoginPassword": { + "value": "[parameters('vmAdminPasswordOrKey')]" + }, + "databases": { + "value": "[parameters('sqlServerDatabases')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "networkIsolation": { + "value": "[parameters('networkIsolation')]" + }, + "virtualNetworkResourceId": "[if(parameters('networkIsolation'), createObject('value', reference('network').outputs.resourceId.value), createObject('value', ''))]", + "virtualNetworkSubnetResourceId": "[if(parameters('networkIsolation'), createObject('value', reference('network').outputs.defaultSubnetResourceId.value), createObject('value', ''))]", + "tags": { + "value": "[variables('allTags')]" + } + }, + "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.1.42791", + "templateHash": "18362011243916437863" + } + }, + "definitions": { + "_1.diagnosticSettingFullType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." } - } - }, - "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": "[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')]" + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } }, - "dependsOn": [ - "privateEndpoint" - ] - }, - "privateEndpoint_privateDnsZoneGroup": { - "condition": "[not(empty(parameters('privateDnsZoneGroup')))]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-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.34.44.8038", - "templateHash": "13997305779829540948" - }, - "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 - } - } - }, - "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." - } - } - }, - "variables": { - "copy": [ - { - "name": "privateDnsZoneConfigsVar", - "count": "[length(parameters('privateDnsZoneConfigs'))]", - "input": { - "name": "[coalesce(tryGet(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')], 'name'), last(split(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId, '/')))]", - "properties": { - "privateDnsZoneId": "[parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId]" - } - } - } - ] - }, - "resources": { - "privateEndpoint": { - "existing": true, - "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2024-05-01", - "name": "[parameters('privateEndpointName')]" - }, - "privateDnsZoneGroup": { - "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2024-05-01", - "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", - "properties": { - "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" - } - } - }, - "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]" - } - } + "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 logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "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." } }, - "dependsOn": [ - "privateEndpoint" - ] + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } } }, - "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-05-01', 'full').location]" - }, - "customDnsConfigs": { - "type": "array", - "items": { - "$ref": "#/definitions/customDnsConfigType" - }, - "metadata": { - "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))]" - } + "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 both logs & metrics are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "_2.databaseSkuType": { + "type": "object", + "properties": { + "capacity": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The capacity of the particular SKU." + } + }, + "family": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. If the service has different generations of hardware, for the same SKU, then that can be captured here." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the SKU, typically, a letter + Number code, e.g. P3." + } + }, + "size": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Size of the particular SKU." + } + }, + "tier": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The tier or edition of the particular SKU, e.g. Basic, Premium." + } + } + }, + "metadata": { + "description": "The database SKU.", + "__bicep_imported_from!": { + "sourceTemplate": "customTypes.bicep" + } + } + }, + "_2.longTermBackupRetentionPolicyType": { + "type": "object", + "properties": { + "backupStorageAccessTier": { + "type": "string", + "allowedValues": [ + "Archive", + "Hot" + ], + "nullable": true, + "metadata": { + "description": "Optional. The BackupStorageAccessTier for the LTR backups." + } + }, + "makeBackupsImmutable": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. The setting whether to make LTR backups immutable." + } + }, + "monthlyRetention": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Monthly retention in ISO 8601 duration format." + } + }, + "weeklyRetention": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Weekly retention in ISO 8601 duration format." + } + }, + "weekOfYear": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Week of year backup to keep for yearly retention." + } + }, + "yearlyRetention": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Yearly retention in ISO 8601 duration format." + } + } + }, + "metadata": { + "description": "The long-term backup retention policy for the database.", + "__bicep_imported_from!": { + "sourceTemplate": "customTypes.bicep" + } + } + }, + "_2.shortTermBackupRetentionPolicyType": { + "type": "object", + "properties": { + "diffBackupIntervalInHours": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Differential backup interval in hours. For Hyperscale tiers this value will be ignored." + } + }, + "retentionDays": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Point-in-time retention in days." + } + } + }, + "metadata": { + "description": "The short-term backup retention policy for the database.", + "__bicep_imported_from!": { + "sourceTemplate": "customTypes.bicep" + } + } + }, + "databasePropertyType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the Elastic Pool." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "sku": { + "$ref": "#/definitions/_2.databaseSkuType", + "nullable": true, + "metadata": { + "description": "Optional. The database SKU." + } + }, + "autoPauseDelay": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Time in minutes after which database is automatically paused. A value of -1 means that automatic pause is disabled." + } + }, + "availabilityZone": { + "type": "string", + "allowedValues": [ + "1", + "2", + "3", + "NoPreference" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specifies the availability zone the database is pinned to." + } + }, + "catalogCollation": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Collation of the metadata catalog." + } + }, + "collation": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The collation of the database." + } + }, + "createMode": { + "type": "string", + "allowedValues": [ + "Copy", + "Default", + "OnlineSecondary", + "PointInTimeRestore", + "Recovery", + "Restore", + "RestoreExternalBackup", + "RestoreExternalBackupSecondary", + "RestoreLongTermRetentionBackup", + "Secondary" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specifies the mode of database creation." + } + }, + "elasticPoolResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource identifier of the elastic pool containing this database." + } + }, + "encryptionProtector": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The azure key vault URI of the database if it's configured with per Database Customer Managed Keys." + } + }, + "encryptionProtectorAutoRotation": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. The flag to enable or disable auto rotation of database encryption protector AKV key." + } + }, + "federatedClientId": { + "type": "string", + "nullable": true, + "minLength": 36, + "maxLength": 36, + "metadata": { + "description": "Optional. The Client id used for cross tenant per database CMK scenario." + } + }, + "freeLimitExhaustionBehavior": { + "type": "string", + "allowedValues": [ + "AutoPause", + "BillOverUsage" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specifies the behavior when monthly free limits are exhausted for the free database." + } + }, + "highAvailabilityReplicaCount": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The number of secondary replicas associated with the database that are used to provide high availability. Not applicable to a Hyperscale database within an elastic pool." + } + }, + "isLedgerOn": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Whether or not this database is a ledger database, which means all tables in the database are ledger tables." + } + }, + "licenseType": { + "type": "string", + "allowedValues": [ + "BasePrice", + "LicenseIncluded" + ], + "nullable": true, + "metadata": { + "description": "Optional. The license type to apply for this database." + } + }, + "longTermRetentionBackupResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource identifier of the long term retention backup associated with create operation of this database." + } + }, + "maintenanceConfigurationId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Maintenance configuration id assigned to the database. This configuration defines the period when the maintenance updates will occur." + } + }, + "manualCutover": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Whether or not customer controlled manual cutover needs to be done during Update Database operation to Hyperscale tier." + } + }, + "maxSizeBytes": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The max size of the database expressed in bytes." + } + }, + "minCapacity": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Minimal capacity that database will always have allocated, if not paused." + } + }, + "performCutover": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. To trigger customer controlled manual cutover during the wait state while Scaling operation is in progress." + } + }, + "preferredEnclaveType": { + "type": "string", + "allowedValues": [ + "Default", + "VBS" + ], + "nullable": true, + "metadata": { + "description": "Optional. Type of enclave requested on the database." + } + }, + "readScale": { + "type": "string", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "nullable": true, + "metadata": { + "description": "Optional. The state of read-only routing. If enabled, connections that have application intent set to readonly in their connection string may be routed to a readonly secondary replica in the same region. Not applicable to a Hyperscale database within an elastic pool." + } + }, + "recoverableDatabaseResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource identifier of the recoverable database associated with create operation of this database." + } + }, + "recoveryServicesRecoveryPointResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource identifier of the recovery point associated with create operation of this database." + } + }, + "requestedBackupStorageRedundancy": { + "type": "string", + "allowedValues": [ + "Geo", + "GeoZone", + "Local", + "Zone" + ], + "nullable": true, + "metadata": { + "description": "Optional. The storage account type to be used to store backups for this database." + } + }, + "restorableDroppedDatabaseResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource identifier of the restorable dropped database associated with create operation of this database." + } + }, + "restorePointInTime": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specifies the point in time (ISO8601 format) of the source database that will be restored to create the new database." + } + }, + "sampleName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the sample schema to apply when creating this database." + } + }, + "secondaryType": { + "type": "string", + "allowedValues": [ + "Geo", + "Named", + "Standby" + ], + "nullable": true, + "metadata": { + "description": "Optional. The secondary type of the database if it is a secondary." + } + }, + "sourceDatabaseDeletionDate": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specifies the time that the database was deleted." + } + }, + "sourceDatabaseResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource identifier of the source database associated with create operation of this database." + } + }, + "sourceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource identifier of the source associated with the create operation of this database." + } + }, + "useFreeLimit": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Whether or not the database uses free monthly limits. Allowed on one database in a subscription." + } + }, + "zoneRedundant": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Whether or not this database is zone redundant, which means the replicas of this database will be spread across multiple availability zones." + } + }, + "diagnosticSettings": { + "type": "array", + "items": { + "$ref": "#/definitions/_1.diagnosticSettingFullType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "backupShortTermRetentionPolicy": { + "$ref": "#/definitions/_2.shortTermBackupRetentionPolicyType", + "nullable": true, + "metadata": { + "description": "Optional. The short term backup retention policy for the database." + } + }, + "backupLongTermRetentionPolicy": { + "$ref": "#/definitions/_2.longTermBackupRetentionPolicyType", + "nullable": true, + "metadata": { + "description": "Optional. The long term backup retention policy for the database." } } }, - "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', 'private-dns-apim-deployment')]", - "[resourceId('Microsoft.Resources/deployments', take(format('{0}-apim-deployment', parameters('name')), 64))]" - ] - } - ], - "outputs": { - "resourceId": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', take(format('{0}-apim-deployment', parameters('name')), 64)), '2022-09-01').outputs.resourceId.value]" - }, - "name": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', take(format('{0}-apim-deployment', parameters('name')), 64)), '2022-09-01').outputs.name.value]" - }, - "privateEndpointId": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', take(format('{0}-apim-private-endpoint-deployment', parameters('name')), 64)), '2022-09-01').outputs.resourceId.value]" + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "customTypes.bicep" + } + } }, - "privateEndpointName": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', take(format('{0}-apim-private-endpoint-deployment', parameters('name')), 64)), '2022-09-01').outputs.name.value]" - } - } - } - }, - "dependsOn": [ - "logAnalyticsWorkspace", - "network" - ] - }, - "cosmosDb": { - "condition": "[parameters('cosmosDbEnabled')]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[take(format('{0}-cosmosdb-deployment', parameters('name')), 64)]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[format('cos{0}{1}', parameters('name'), variables('resourceToken'))]" - }, - "location": { - "value": "[parameters('location')]" - }, - "networkIsolation": { - "value": "[parameters('networkIsolation')]" - }, - "virtualNetworkResourceId": "[if(parameters('networkIsolation'), createObject('value', reference('network').outputs.virtualNetworkId.value), createObject('value', ''))]", - "virtualNetworkSubnetResourceId": "[if(parameters('networkIsolation'), createObject('value', reference('network').outputs.vmSubnetId.value), createObject('value', ''))]", - "logAnalyticsWorkspaceResourceId": { - "value": "[reference('logAnalyticsWorkspace').outputs.resourceId.value]" - }, - "databases": { - "value": "[parameters('cosmosDatabases')]" - }, - "tags": { - "value": "[variables('allTags')]" - } - }, - "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.1.42791", - "templateHash": "7173901627480655800" - } - }, - "definitions": { "roleAssignmentType": { "type": "object", "properties": { @@ -69950,186 +83172,13 @@ "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } - }, - "sqlDatabaseType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. Name of the SQL database ." - } - }, - "throughput": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. Default to 400. Request units per second. Will be ignored if autoscaleSettingsMaxThroughput is used. Setting throughput at the database level is only recommended for development/test or when workload across all containers in the shared throughput database is uniform. For best performance for large production workloads, it is recommended to set dedicated throughput (autoscale or manual) at the container level and not at the database level." - } - }, - "autoscaleSettingsMaxThroughput": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. Specifies the Autoscale settings and represents maximum throughput, the resource can scale up to. The autoscale throughput should have valid throughput values between 1000 and 1000000 inclusive in increments of 1000. If value is set to null, then autoscale will be disabled. Setting throughput at the database level is only recommended for development/test or when workload across all containers in the shared throughput database is uniform. For best performance for large production workloads, it is recommended to set dedicated throughput (autoscale or manual) at the container level and not at the database level." - } - }, - "containers": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. Name of the container." - } - }, - "paths": { - "type": "array", - "items": { - "type": "string" - }, - "minLength": 1, - "maxLength": 3, - "metadata": { - "description": "Required. List of paths using which data within the container can be partitioned. For kind=MultiHash it can be up to 3. For anything else it needs to be exactly 1." - } - }, - "analyticalStorageTtl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. Default to 0. Indicates how long data should be retained in the analytical store, for a container. Analytical store is enabled when ATTL is set with a value other than 0. If the value is set to -1, the analytical store retains all historical data, irrespective of the retention of the data in the transactional store." - } - }, - "autoscaleSettingsMaxThroughput": { - "type": "int", - "nullable": true, - "maxValue": 1000000, - "metadata": { - "description": "Optional. Specifies the Autoscale settings and represents maximum throughput, the resource can scale up to. The autoscale throughput should have valid throughput values between 1000 and 1000000 inclusive in increments of 1000. If value is set to null, then autoscale will be disabled. For best performance for large production workloads, it is recommended to set dedicated throughput (autoscale or manual) at the container level." - } - }, - "conflictResolutionPolicy": { - "type": "object", - "properties": { - "conflictResolutionPath": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Conditional. The conflict resolution path in the case of LastWriterWins mode. Required if `mode` is set to 'LastWriterWins'." - } - }, - "conflictResolutionProcedure": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Conditional. The procedure to resolve conflicts in the case of custom mode. Required if `mode` is set to 'Custom'." - } - }, - "mode": { - "type": "string", - "allowedValues": [ - "Custom", - "LastWriterWins" - ], - "metadata": { - "description": "Required. Indicates the conflict resolution mode." - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. The conflict resolution policy for the container. Conflicts and conflict resolution policies are applicable if the Azure Cosmos DB account is configured with multiple write regions." - } - }, - "defaultTtl": { - "type": "int", - "nullable": true, - "minValue": -1, - "maxValue": 2147483647, - "metadata": { - "description": "Optional. Default to -1. Default time to live (in seconds). With Time to Live or TTL, Azure Cosmos DB provides the ability to delete items automatically from a container after a certain time period. If the value is set to \"-1\", it is equal to infinity, and items don't expire by default." - } - }, - "indexingPolicy": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Indexing policy of the container." - } - }, - "kind": { - "type": "string", - "allowedValues": [ - "Hash", - "MultiHash" - ], - "nullable": true, - "metadata": { - "description": "Optional. Default to Hash. Indicates the kind of algorithm used for partitioning." - } - }, - "version": { - "type": "int", - "allowedValues": [ - 1, - 2 - ], - "nullable": true, - "metadata": { - "description": "Optional. Default to 1 for Hash and 2 for MultiHash - 1 is not allowed for MultiHash. Version of the partition key definition." - } - }, - "throughput": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. Default to 400. Request Units per second. Will be ignored if autoscaleSettingsMaxThroughput is used." - } - }, - "uniqueKeyPolicyKeys": { - "type": "array", - "items": { - "type": "object", - "properties": { - "paths": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. List of paths must be unique for each document in the Azure Cosmos DB service." - } - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. The unique key policy configuration containing a list of unique keys that enforces uniqueness constraint on documents in the collection in the Azure Cosmos DB service." - } - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. Array of containers to deploy in the SQL database." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "customTypes.bicep" - } - } } }, "parameters": { "name": { "type": "string", "metadata": { - "description": "Name of the Cosmos DB Account." + "description": "Name of the SQL Server instance." } }, "location": { @@ -70145,29 +83194,35 @@ "description": "Optional. Tags to be applied to the resources." } }, - "virtualNetworkResourceId": { + "administratorLogin": { "type": "string", "metadata": { - "description": "Resource ID of the virtual network to link the private DNS zones." + "description": "Username for the SQL Server administrator." } }, - "virtualNetworkSubnetResourceId": { + "administratorLoginPassword": { + "type": "securestring", + "metadata": { + "description": "Password for the SQL Server administrator." + } + }, + "virtualNetworkResourceId": { "type": "string", "metadata": { - "description": "Resource ID of the subnet for the private endpoint." + "description": "Resource ID of the virtual network to link the private DNS zones." } }, - "logAnalyticsWorkspaceResourceId": { + "virtualNetworkSubnetResourceId": { "type": "string", "metadata": { - "description": "Resource ID of the Log Analytics workspace to use for diagnostic settings." + "description": "Resource ID of the subnet for the private endpoint." } }, "networkIsolation": { "type": "bool", "defaultValue": true, "metadata": { - "description": "Specifies whether network isolation is enabled. This will create a private endpoint for the Cosmos DB Account and link the private DNS zone." + "description": "Specifies whether network isolation is enabled. This will create a private endpoint for the SQL Server instance and link the private DNS zone." } }, "roleAssignments": { @@ -70183,11 +83238,11 @@ "databases": { "type": "array", "items": { - "$ref": "#/definitions/sqlDatabaseType" + "$ref": "#/definitions/databasePropertyType" }, "nullable": true, "metadata": { - "description": "Optional. List of Cosmos DB databases to deploy." + "description": "Optional. List of SQL Server databases to deploy." } } }, @@ -70199,7 +83254,7 @@ "condition": "[parameters('networkIsolation')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "private-dns-cosmosdb-deployment", + "name": "private-dns-sql-deployment", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -70207,7 +83262,7 @@ "mode": "Incremental", "parameters": { "name": { - "value": "privatelink.documents.azure.com" + "value": "[format('privatelink{0}', environment().suffixes.sqlServerHostname)]" }, "virtualNetworkLinks": { "value": [ @@ -73238,10 +86293,10 @@ } } }, - "cosmosDb": { + "sqlServer": { "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[take(format('{0}-cosmosdb-deployment', variables('nameFormatted')), 64)]", + "name": "[take(format('{0}-sqlserver-deployment', variables('nameFormatted')), 64)]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -73251,44 +86306,30 @@ "name": { "value": "[variables('nameFormatted')]" }, - "automaticFailover": { - "value": true - }, - "diagnosticSettings": { - "value": [ - { - "workspaceResourceId": "[parameters('logAnalyticsWorkspaceResourceId')]" - } - ] + "administratorLogin": { + "value": "[parameters('administratorLogin')]" }, - "disableKeyBasedMetadataWriteAccess": { - "value": true + "administratorLoginPassword": { + "value": "[parameters('administratorLoginPassword')]" }, - "disableLocalAuth": { - "value": true + "databases": { + "value": "[parameters('databases')]" }, "location": { "value": "[parameters('location')]" }, - "minimumTlsVersion": { - "value": "Tls12" - }, - "defaultConsistencyLevel": { - "value": "Session" - }, - "networkRestrictions": { + "managedIdentities": { "value": { - "networkAclBypass": "None", - "publicNetworkAccess": "[if(parameters('networkIsolation'), 'Disabled', 'Enabled')]" + "systemAssigned": true } }, - "privateEndpoints": "[if(parameters('networkIsolation'), createObject('value', createArray(createObject('privateDnsZoneGroup', createObject('privateDnsZoneGroupConfigs', createArray(createObject('privateDnsZoneResourceId', reference('privateDnsZone').outputs.resourceId.value))), 'service', 'Sql', 'subnetResourceId', parameters('virtualNetworkSubnetResourceId')))), createObject('value', createArray()))]", - "sqlDatabases": { - "value": "[parameters('databases')]" + "restrictOutboundNetworkAccess": { + "value": "Disabled" }, "roleAssignments": { "value": "[parameters('roleAssignments')]" }, + "privateEndpoints": "[if(parameters('networkIsolation'), createObject('value', createArray(createObject('privateDnsZoneGroup', createObject('privateDnsZoneGroupConfigs', createArray(createObject('privateDnsZoneResourceId', reference('privateDnsZone').outputs.resourceId.value))), 'service', 'sqlServer', 'subnetResourceId', parameters('virtualNetworkSubnetResourceId')))), createObject('value', createArray()))]", "tags": { "value": "[parameters('tags')]" } @@ -73300,807 +86341,440 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "10926873725172699375" + "version": "0.34.44.8038", + "templateHash": "5205596651564295650" }, - "name": "DocumentDB Database Accounts", - "description": "This module deploys a DocumentDB Database Account." + "name": "Azure SQL Servers", + "description": "This module deploys an Azure SQL Server." }, "definitions": { - "managedIdentitiesType": { + "privateEndpointOutputType": { "type": "object", "properties": { - "systemAssigned": { - "type": "bool", - "nullable": true, + "name": { + "type": "string", "metadata": { - "description": "Optional. Enables system assigned managed identity on the resource." + "description": "The name of the private endpoint." } }, - "userAssignedResourceIds": { - "type": "array", - "items": { - "type": "string" - }, - "nullable": true, - "metadata": { - "description": "Optional. The resource ID(s) to assign to the resource." - } - } - }, - "nullable": true - }, - "lockType": { - "type": "object", - "properties": { - "name": { + "resourceId": { "type": "string", - "nullable": true, "metadata": { - "description": "Optional. Specify the name of lock." + "description": "The resource ID of the private endpoint." } }, - "kind": { + "groupId": { "type": "string", - "allowedValues": [ - "CanNotDelete", - "None", - "ReadOnly" - ], "nullable": true, "metadata": { - "description": "Optional. Specify the type of lock." - } - } - }, - "nullable": true - }, - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } + "description": "The group Id for the private endpoint Group." } - } - }, - "nullable": true - }, - "privateEndpointType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the private endpoint." - } - }, - "location": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The location to deploy the private endpoint to." - } - }, - "privateLinkServiceConnectionName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the private link connection to create." - } - }, - "service": { - "type": "string", - "metadata": { - "description": "Required. The subresource to deploy the private endpoint for. For example \"blob\", \"table\", \"queue\" or \"file\"." - } - }, - "subnetResourceId": { - "type": "string", - "metadata": { - "description": "Required. Resource ID of the subnet where the endpoint needs to be created." - } - }, - "privateDnsZoneGroup": { + }, + "customDnsConfigs": { + "type": "array", + "items": { "type": "object", "properties": { - "name": { + "fqdn": { "type": "string", "nullable": true, "metadata": { - "description": "Optional. The name of the Private DNS Zone Group." + "description": "FQDN that resolves to private endpoint IP address." } }, - "privateDnsZoneGroupConfigs": { + "ipAddresses": { "type": "array", - "items": { - "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": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. The private DNS zone group to configure for the private endpoint." - } - }, - "isManualConnection": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. If Manual Private Link Connection is required." - } - }, - "manualConnectionRequestMessage": { - "type": "string", - "nullable": true, - "maxLength": 140, - "metadata": { - "description": "Optional. A message passed to the owner of the remote resource with the manual connection request." - } - }, - "customDnsConfigs": { - "type": "array", - "items": { - "type": "object", - "properties": { - "fqdn": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. FQDN that resolves to private endpoint IP address." - } - }, - "ipAddresses": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. A list of private ip addresses of the private endpoint." - } - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. Custom DNS configurations." - } - }, - "ipConfigurations": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the resource that is unique within a resource group." - } - }, - "properties": { - "type": "object", - "properties": { - "groupId": { - "type": "string", - "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." - } - }, - "memberName": { - "type": "string", - "metadata": { - "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to." - } - }, - "privateIPAddress": { - "type": "string", - "metadata": { - "description": "Required. A private ip address obtained from the private endpoint's subnet." - } - } - }, - "metadata": { - "description": "Required. Properties of private endpoint IP configurations." - } - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." - } - }, - "applicationSecurityGroupResourceIds": { - "type": "array", - "items": { - "type": "string" - }, - "nullable": true, - "metadata": { - "description": "Optional. Application security groups in which the private endpoint IP configuration is included." - } - }, - "customNetworkInterfaceName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The custom name of the network interface attached to the private endpoint." - } - }, - "lock": { - "$ref": "#/definitions/lockType", - "metadata": { - "description": "Optional. Specify the type of lock." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "tags": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." - } - }, - "enableTelemetry": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Enable/Disable usage telemetry for module." - } - }, - "resourceGroupName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Specify if you want to deploy the Private Endpoint into a different resource group than the main resource." - } - } - } - }, - "nullable": true - }, - "diagnosticSettingType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of diagnostic setting." - } - }, - "logCategoriesAndGroups": { - "type": "array", - "items": { - "type": "object", - "properties": { - "category": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." - } - }, - "categoryGroup": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." - } - }, - "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 logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." - } - }, - "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." - } + "items": { + "type": "string" }, - "enabled": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Enable or disable the category explicitly. Default is `true`." - } + "metadata": { + "description": "A list of private IP addresses of the private endpoint." } } - }, - "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." - } + "metadata": { + "description": "The custom DNS configurations of the private endpoint." + } + }, + "networkInterfaceResourceIds": { + "type": "array", + "items": { + "type": "string" }, - "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": "The IDs of the network interfaces associated with the private endpoint." } } }, - "nullable": true + "metadata": { + "__bicep_export!": true, + "description": "The type for a private endpoint output." + } }, - "failoverLocationsType": { + "auditSettingsType": { "type": "object", "properties": { - "failoverPriority": { - "type": "int", + "name": { + "type": "string", + "nullable": true, "metadata": { - "description": "Required. The failover priority of the region. A failover priority of 0 indicates a write region. The maximum value for a failover priority = (total number of regions - 1). Failover priority values must be unique for each of the regions in which the database account exists." + "description": "Optional. Specifies the name of the audit settings." } }, - "isZoneRedundant": { + "auditActionsAndGroups": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Specifies the Actions-Groups and Actions to audit." + } + }, + "isAzureMonitorTargetEnabled": { "type": "bool", "nullable": true, "metadata": { - "description": "Optional. Default to true. Flag to indicate whether or not this region is an AvailabilityZone region." + "description": "Optional. Specifies whether audit events are sent to Azure Monitor." } }, - "locationName": { + "isDevopsAuditEnabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Specifies the state of devops audit. If state is Enabled, devops logs will be sent to Azure Monitor." + } + }, + "isManagedIdentityInUse": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Specifies whether Managed Identity is used to access blob storage." + } + }, + "isStorageSecondaryKeyInUse": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Specifies whether storageAccountAccessKey value is the storage's secondary key." + } + }, + "queueDelayMs": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Specifies the amount of time in milliseconds that can elapse before audit actions are forced to be processed." + } + }, + "retentionDays": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Specifies the number of days to keep in the audit logs in the storage account." + } + }, + "state": { "type": "string", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "nullable": true, "metadata": { - "description": "Required. The name of the region." + "description": "Optional. Specifies the state of the audit. If state is Enabled, storageEndpoint or isAzureMonitorTargetEnabled are required." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specifies the identifier key of the auditing storage account." } } + }, + "metadata": { + "__bicep_export!": true } }, - "sqlRoleDefinitionsType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. Name of the SQL Role Definition." - } - }, - "dataAction": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. An array of data actions that are allowed." - } - }, - "roleName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. A user-friendly name for the Role Definition. Must be unique for the database account." - } - }, - "roleType": { - "type": "string", - "allowedValues": [ - "BuiltInRole", - "CustomRole" - ], - "nullable": true, - "metadata": { - "description": "Optional. Indicates whether the Role Definition was built-in or user created." - } + "secretsExportConfigurationType": { + "type": "object", + "properties": { + "keyVaultResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the key vault where to store the secrets of this module." + } + }, + "sqlAdminPasswordSecretName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The sqlAdminPassword secret name to create." + } + }, + "sqlAzureConnectionStringSercretName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The sqlAzureConnectionString secret name to create." } } }, - "nullable": true + "metadata": { + "__bicep_export!": true + } }, - "sqlDatabaseType": { + "serverExternalAdministratorType": { "type": "object", "properties": { - "name": { + "administratorType": { "type": "string", + "allowedValues": [ + "ActiveDirectory" + ], + "nullable": true, "metadata": { - "description": "Required. Name of the SQL database ." + "description": "Optional. Type of the sever administrator." } }, - "throughput": { - "type": "int", - "nullable": true, + "azureADOnlyAuthentication": { + "type": "bool", "metadata": { - "description": "Optional. Default to 400. Request units per second. Will be ignored if autoscaleSettingsMaxThroughput is used. Setting throughput at the database level is only recommended for development/test or when workload across all containers in the shared throughput database is uniform. For best performance for large production workloads, it is recommended to set dedicated throughput (autoscale or manual) at the container level and not at the database level." + "description": "Required. Azure Active Directory only Authentication enabled." } }, - "autoscaleSettingsMaxThroughput": { - "type": "int", - "nullable": true, + "login": { + "type": "string", "metadata": { - "description": "Optional. Specifies the Autoscale settings and represents maximum throughput, the resource can scale up to. The autoscale throughput should have valid throughput values between 1000 and 1000000 inclusive in increments of 1000. If value is set to null, then autoscale will be disabled. Setting throughput at the database level is only recommended for development/test or when workload across all containers in the shared throughput database is uniform. For best performance for large production workloads, it is recommended to set dedicated throughput (autoscale or manual) at the container level and not at the database level." + "description": "Required. Login name of the server administrator." } }, - "containers": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. Name of the container." - } - }, - "paths": { - "type": "array", - "items": { - "type": "string" - }, - "minLength": 1, - "maxLength": 3, - "metadata": { - "description": "Required. List of paths using which data within the container can be partitioned. For kind=MultiHash it can be up to 3. For anything else it needs to be exactly 1." - } - }, - "analyticalStorageTtl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. Default to 0. Indicates how long data should be retained in the analytical store, for a container. Analytical store is enabled when ATTL is set with a value other than 0. If the value is set to -1, the analytical store retains all historical data, irrespective of the retention of the data in the transactional store." - } - }, - "autoscaleSettingsMaxThroughput": { - "type": "int", - "nullable": true, - "maxValue": 1000000, - "metadata": { - "description": "Optional. Specifies the Autoscale settings and represents maximum throughput, the resource can scale up to. The autoscale throughput should have valid throughput values between 1000 and 1000000 inclusive in increments of 1000. If value is set to null, then autoscale will be disabled. For best performance for large production workloads, it is recommended to set dedicated throughput (autoscale or manual) at the container level." - } - }, - "conflictResolutionPolicy": { - "type": "object", - "properties": { - "conflictResolutionPath": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Conditional. The conflict resolution path in the case of LastWriterWins mode. Required if `mode` is set to 'LastWriterWins'." - } - }, - "conflictResolutionProcedure": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Conditional. The procedure to resolve conflicts in the case of custom mode. Required if `mode` is set to 'Custom'." - } - }, - "mode": { - "type": "string", - "allowedValues": [ - "Custom", - "LastWriterWins" - ], - "metadata": { - "description": "Required. Indicates the conflict resolution mode." - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. The conflict resolution policy for the container. Conflicts and conflict resolution policies are applicable if the Azure Cosmos DB account is configured with multiple write regions." - } - }, - "defaultTtl": { - "type": "int", - "nullable": true, - "minValue": -1, - "maxValue": 2147483647, - "metadata": { - "description": "Optional. Default to -1. Default time to live (in seconds). With Time to Live or TTL, Azure Cosmos DB provides the ability to delete items automatically from a container after a certain time period. If the value is set to \"-1\", it is equal to infinity, and items don't expire by default." - } - }, - "indexingPolicy": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Indexing policy of the container." - } - }, - "kind": { - "type": "string", - "allowedValues": [ - "Hash", - "MultiHash" - ], - "nullable": true, - "metadata": { - "description": "Optional. Default to Hash. Indicates the kind of algorithm used for partitioning." - } - }, - "version": { - "type": "int", - "allowedValues": [ - 1, - 2 - ], - "nullable": true, - "metadata": { - "description": "Optional. Default to 1 for Hash and 2 for MultiHash - 1 is not allowed for MultiHash. Version of the partition key definition." - } - }, - "throughput": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. Default to 400. Request Units per second. Will be ignored if autoscaleSettingsMaxThroughput is used." - } - }, - "uniqueKeyPolicyKeys": { - "type": "array", - "items": { - "type": "object", - "properties": { - "paths": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. List of paths must be unique for each document in the Azure Cosmos DB service." - } - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. The unique key policy configuration containing a list of unique keys that enforces uniqueness constraint on documents in the collection in the Azure Cosmos DB service." - } - } - } - }, + "principalType": { + "type": "string", + "allowedValues": [ + "Application", + "Group", + "User" + ], + "metadata": { + "description": "Required. Principal Type of the sever administrator." + } + }, + "sid": { + "type": "string", + "metadata": { + "description": "Required. SID (object ID) of the server administrator." + } + }, + "tenantId": { + "type": "string", "nullable": true, "metadata": { - "description": "Optional. Array of containers to deploy in the SQL database." + "description": "Optional. Tenant ID of the administrator." } } + }, + "metadata": { + "__bicep_export!": true } }, - "secretsExportConfigurationType": { + "databasePropertyType": { "type": "object", "properties": { - "keyVaultResourceId": { + "name": { "type": "string", "metadata": { - "description": "Required. The resource ID of the key vault where to store the secrets of this module." + "description": "Required. The name of the Elastic Pool." } }, - "primaryWriteKeySecretName": { + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "sku": { + "$ref": "#/definitions/databaseSkuType", + "nullable": true, + "metadata": { + "description": "Optional. The database SKU." + } + }, + "autoPauseDelay": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Time in minutes after which database is automatically paused. A value of -1 means that automatic pause is disabled." + } + }, + "availabilityZone": { "type": "string", + "allowedValues": [ + "1", + "2", + "3", + "NoPreference" + ], "nullable": true, "metadata": { - "description": "Optional. The primary write key secret name to create." + "description": "Optional. Specifies the availability zone the database is pinned to." } }, - "primaryReadOnlyKeySecretName": { + "catalogCollation": { "type": "string", "nullable": true, "metadata": { - "description": "Optional. The primary readonly key secret name to create." + "description": "Optional. Collation of the metadata catalog." } }, - "primaryWriteConnectionStringSecretName": { + "collation": { "type": "string", "nullable": true, "metadata": { - "description": "Optional. The primary write connection string secret name to create." + "description": "Optional. The collation of the database." } }, - "primaryReadonlyConnectionStringSecretName": { + "createMode": { "type": "string", + "allowedValues": [ + "Copy", + "Default", + "OnlineSecondary", + "PointInTimeRestore", + "Recovery", + "Restore", + "RestoreExternalBackup", + "RestoreExternalBackupSecondary", + "RestoreLongTermRetentionBackup", + "Secondary" + ], "nullable": true, "metadata": { - "description": "Optional. The primary readonly connection string secret name to create." + "description": "Optional. Specifies the mode of database creation." } }, - "secondaryWriteKeySecretName": { + "elasticPoolResourceId": { "type": "string", "nullable": true, "metadata": { - "description": "Optional. The primary write key secret name to create." + "description": "Optional. The resource identifier of the elastic pool containing this database." } }, - "secondaryReadonlyKeySecretName": { + "encryptionProtector": { "type": "string", "nullable": true, "metadata": { - "description": "Optional. The primary readonly key secret name to create." + "description": "Optional. The azure key vault URI of the database if it's configured with per Database Customer Managed Keys." } }, - "secondaryWriteConnectionStringSecretName": { + "encryptionProtectorAutoRotation": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. The flag to enable or disable auto rotation of database encryption protector AKV key." + } + }, + "federatedClientId": { + "type": "string", + "nullable": true, + "minLength": 36, + "maxLength": 36, + "metadata": { + "description": "Optional. The Client id used for cross tenant per database CMK scenario." + } + }, + "freeLimitExhaustionBehavior": { + "type": "string", + "allowedValues": [ + "AutoPause", + "BillOverUsage" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specifies the behavior when monthly free limits are exhausted for the free database." + } + }, + "highAvailabilityReplicaCount": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The number of secondary replicas associated with the database that are used to provide high availability. Not applicable to a Hyperscale database within an elastic pool." + } + }, + "isLedgerOn": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Whether or not this database is a ledger database, which means all tables in the database are ledger tables." + } + }, + "licenseType": { + "type": "string", + "allowedValues": [ + "BasePrice", + "LicenseIncluded" + ], + "nullable": true, + "metadata": { + "description": "Optional. The license type to apply for this database." + } + }, + "longTermRetentionBackupResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource identifier of the long term retention backup associated with create operation of this database." + } + }, + "maintenanceConfigurationId": { "type": "string", "nullable": true, "metadata": { - "description": "Optional. The primary write connection string secret name to create." + "description": "Optional. Maintenance configuration id assigned to the database. This configuration defines the period when the maintenance updates will occur." + } + }, + "manualCutover": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Whether or not customer controlled manual cutover needs to be done during Update Database operation to Hyperscale tier." + } + }, + "maxSizeBytes": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The max size of the database expressed in bytes." } }, - "secondaryReadonlyConnectionStringSecretName": { + "minCapacity": { "type": "string", "nullable": true, "metadata": { - "description": "Optional. The primary readonly connection string secret name to create." + "description": "Optional. Minimal capacity that database will always have allocated, if not paused." } - } - } - }, - "secretsOutputType": { - "type": "object", - "properties": {}, - "additionalProperties": { - "$ref": "#/definitions/secretSetType", - "metadata": { - "description": "An exported secret's references." - } - } - }, - "networkRestrictionsType": { - "type": "object", - "properties": { - "ipRules": { - "type": "array", - "items": { - "type": "string" - }, + }, + "performCutover": { + "type": "bool", "nullable": true, "metadata": { - "description": "Optional. A single IPv4 address or a single IPv4 address range in CIDR format. Provided IPs must be well-formatted and cannot be contained in one of the following ranges: 10.0.0.0/8, 100.64.0.0/10, 172.16.0.0/12, 192.168.0.0/16, since these are not enforceable by the IP address filter. Example of valid inputs: \"23.40.210.245\" or \"23.40.210.0/8\"." + "description": "Optional. To trigger customer controlled manual cutover during the wait state while Scaling operation is in progress." } }, - "networkAclBypass": { + "preferredEnclaveType": { "type": "string", "allowedValues": [ - "AzureServices", - "None" + "Default", + "VBS" ], "nullable": true, "metadata": { - "description": "Optional. Default to None. Specifies the network ACL bypass for Azure services." + "description": "Optional. Type of enclave requested on the database." } }, - "publicNetworkAccess": { + "readScale": { "type": "string", "allowedValues": [ "Disabled", @@ -74108,4798 +86782,4157 @@ ], "nullable": true, "metadata": { - "description": "Optional. Default to Disabled. Whether requests from Public Network are allowed." + "description": "Optional. The state of read-only routing. If enabled, connections that have application intent set to readonly in their connection string may be routed to a readonly secondary replica in the same region. Not applicable to a Hyperscale database within an elastic pool." } }, - "virtualNetworkRules": { - "type": "array", - "items": { - "type": "object", - "properties": { - "subnetResourceId": { - "type": "string", - "metadata": { - "description": "Required. Resource ID of a subnet." - } - } - } - }, + "recoverableDatabaseResourceId": { + "type": "string", "nullable": true, "metadata": { - "description": "Optional. List of Virtual Network ACL rules configured for the Cosmos DB account.." + "description": "Optional. The resource identifier of the recoverable database associated with create operation of this database." } - } - } - }, - "secretSetType": { - "type": "object", - "properties": { - "secretResourceId": { + }, + "recoveryServicesRecoveryPointResourceId": { "type": "string", + "nullable": true, "metadata": { - "description": "The resourceId of the exported secret." + "description": "Optional. The resource identifier of the recovery point associated with create operation of this database." } }, - "secretUri": { + "requestedBackupStorageRedundancy": { "type": "string", + "allowedValues": [ + "Geo", + "GeoZone", + "Local", + "Zone" + ], + "nullable": true, "metadata": { - "description": "The secret URI of the exported secret." + "description": "Optional. The storage account type to be used to store backups for this database." } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "modules/keyVaultExport.bicep" - } - } - } - }, - "parameters": { - "name": { - "type": "string", - "metadata": { - "description": "Required. Name of the Database Account." - } - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]", - "metadata": { - "description": "Optional. Default to current resource group scope location. Location for all resources." - } - }, - "tags": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Tags of the Database Account resource." - } - }, - "managedIdentities": { - "$ref": "#/definitions/managedIdentitiesType", - "metadata": { - "description": "Optional. The managed identity definition for this resource." - } - }, - "databaseAccountOfferType": { - "type": "string", - "defaultValue": "Standard", - "allowedValues": [ - "Standard" - ], - "metadata": { - "description": "Optional. Default to Standard. The offer type for the Azure Cosmos DB database account." - } - }, - "locations": { - "type": "array", - "items": { - "$ref": "#/definitions/failoverLocationsType" - }, - "defaultValue": [], - "metadata": { - "description": "Optional. Default to the location where the account is deployed. Locations enabled for the Cosmos DB account." - } - }, - "defaultConsistencyLevel": { - "type": "string", - "defaultValue": "Session", - "allowedValues": [ - "Eventual", - "ConsistentPrefix", - "Session", - "BoundedStaleness", - "Strong" - ], - "metadata": { - "description": "Optional. Default to Session. The default consistency level of the Cosmos DB account." - } - }, - "disableLocalAuth": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Optional. Default to true. Opt-out of local authentication and ensure only MSI and AAD can be used exclusively for authentication." - } - }, - "enableAnalyticalStorage": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "Optional. Default to false. Flag to indicate whether to enable storage analytics." - } - }, - "automaticFailover": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Optional. Default to true. Enable automatic failover for regions." - } - }, - "enableFreeTier": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "Optional. Default to false. Flag to indicate whether Free Tier is enabled." - } - }, - "enableMultipleWriteLocations": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "Optional. Default to false. Enables the account to write in multiple locations. Periodic backup must be used if enabled." - } - }, - "disableKeyBasedMetadataWriteAccess": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Optional. Default to true. Disable write operations on metadata resources (databases, containers, throughput) via account keys." - } - }, - "maxStalenessPrefix": { - "type": "int", - "defaultValue": 100000, - "minValue": 1, - "maxValue": 2147483647, - "metadata": { - "description": "Optional. Default to 100000. Max stale requests. Required for BoundedStaleness. Valid ranges, Single Region: 10 to 1000000. Multi Region: 100000 to 1000000." - } - }, - "maxIntervalInSeconds": { - "type": "int", - "defaultValue": 300, - "minValue": 5, - "maxValue": 86400, - "metadata": { - "description": "Optional. Default to 300. Max lag time (minutes). Required for BoundedStaleness. Valid ranges, Single Region: 5 to 84600. Multi Region: 300 to 86400." - } - }, - "serverVersion": { - "type": "string", - "defaultValue": "4.2", - "allowedValues": [ - "3.2", - "3.6", - "4.0", - "4.2", - "5.0", - "6.0", - "7.0" - ], - "metadata": { - "description": "Optional. Default to 4.2. Specifies the MongoDB server version to use." - } - }, - "sqlDatabases": { - "type": "array", - "items": { - "$ref": "#/definitions/sqlDatabaseType" - }, - "defaultValue": [], - "metadata": { - "description": "Optional. SQL Databases configurations." - } - }, - "sqlRoleAssignmentsPrincipalIds": { - "type": "array", - "defaultValue": [], - "metadata": { - "description": "Optional. SQL Role Definitions configurations." - } - }, - "sqlRoleDefinitions": { - "$ref": "#/definitions/sqlRoleDefinitionsType", - "metadata": { - "description": "Optional. SQL Role Definitions configurations." - } - }, - "mongodbDatabases": { - "type": "array", - "defaultValue": [], - "metadata": { - "description": "Optional. MongoDB Databases configurations." - } - }, - "gremlinDatabases": { - "type": "array", - "defaultValue": [], - "metadata": { - "description": "Optional. Gremlin Databases configurations." - } - }, - "tables": { - "type": "array", - "defaultValue": [], - "metadata": { - "description": "Optional. Table configurations." - } - }, - "enableTelemetry": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Optional. Enable/Disable usage telemetry for module." - } - }, - "totalThroughputLimit": { - "type": "int", - "defaultValue": -1, - "metadata": { - "description": "Optional. Default to unlimited. The total throughput limit imposed on this Cosmos DB account (RU/s)." - } - }, - "lock": { - "$ref": "#/definitions/lockType", - "metadata": { - "description": "Optional. The lock settings of the service." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalIds' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." - } - }, - "diagnosticSettings": { - "$ref": "#/definitions/diagnosticSettingType", - "metadata": { - "description": "Optional. The diagnostic settings of the service." - } - }, - "capabilitiesToAdd": { - "type": "array", - "items": { - "type": "string" - }, - "defaultValue": [], - "allowedValues": [ - "EnableCassandra", - "EnableTable", - "EnableGremlin", - "EnableMongo", - "DisableRateLimitingResponses", - "EnableServerless", - "EnableNoSQLVectorSearch", - "EnableNoSQLFullTextSearch", - "EnableMaterializedViews" - ], - "metadata": { - "description": "Optional. List of Cosmos DB capabilities for the account." - } - }, - "backupPolicyType": { - "type": "string", - "defaultValue": "Continuous", - "allowedValues": [ - "Periodic", - "Continuous" - ], - "metadata": { - "description": "Optional. Default to Continuous. Describes the mode of backups. Periodic backup must be used if multiple write locations are used." - } - }, - "backupPolicyContinuousTier": { - "type": "string", - "defaultValue": "Continuous30Days", - "allowedValues": [ - "Continuous30Days", - "Continuous7Days" - ], - "metadata": { - "description": "Optional. Default to Continuous30Days. Configuration values for continuous mode backup." - } - }, - "backupIntervalInMinutes": { - "type": "int", - "defaultValue": 240, - "minValue": 60, - "maxValue": 1440, - "metadata": { - "description": "Optional. Default to 240. An integer representing the interval in minutes between two backups. Only applies to periodic backup type." - } - }, - "backupRetentionIntervalInHours": { - "type": "int", - "defaultValue": 8, - "minValue": 2, - "maxValue": 720, - "metadata": { - "description": "Optional. Default to 8. An integer representing the time (in hours) that each backup is retained. Only applies to periodic backup type." - } - }, - "backupStorageRedundancy": { - "type": "string", - "defaultValue": "Local", - "allowedValues": [ - "Geo", - "Local", - "Zone" - ], - "metadata": { - "description": "Optional. Default to Local. Enum to indicate type of backup residency. Only applies to periodic backup type." - } - }, - "privateEndpoints": { - "$ref": "#/definitions/privateEndpointType", - "metadata": { - "description": "Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible." - } - }, - "secretsExportConfiguration": { - "$ref": "#/definitions/secretsExportConfigurationType", - "nullable": true, - "metadata": { - "description": "Optional. Key vault reference and secret settings for the module's secrets export." - } - }, - "networkRestrictions": { - "$ref": "#/definitions/networkRestrictionsType", - "defaultValue": { - "ipRules": [], - "virtualNetworkRules": [], - "publicNetworkAccess": "Disabled" - }, - "metadata": { - "description": "Optional. The network configuration of this module. Defaults to `{ ipRules: [], virtualNetworkRules: [], publicNetworkAccess: 'Disabled' }`." - } - }, - "minimumTlsVersion": { - "type": "string", - "defaultValue": "Tls12", - "allowedValues": [ - "Tls12" - ], - "metadata": { - "description": "Optional. Default to TLS 1.2. Enum to indicate the minimum allowed TLS version. Azure Cosmos DB for MongoDB RU and Apache Cassandra only work with TLS 1.2 or later." - } - } - }, - "variables": { - "copy": [ - { - "name": "databaseAccount_locations", - "count": "[length(parameters('locations'))]", - "input": { - "failoverPriority": "[parameters('locations')[copyIndex('databaseAccount_locations')].failoverPriority]", - "locationName": "[parameters('locations')[copyIndex('databaseAccount_locations')].locationName]", - "isZoneRedundant": "[coalesce(tryGet(parameters('locations')[copyIndex('databaseAccount_locations')], 'isZoneRedundant'), true())]" - } - }, - { - "name": "capabilities", - "count": "[length(parameters('capabilitiesToAdd'))]", - "input": { - "name": "[parameters('capabilitiesToAdd')[copyIndex('capabilities')]]" - } - }, - { - "name": "ipRules", - "count": "[length(coalesce(tryGet(parameters('networkRestrictions'), 'ipRules'), createArray()))]", - "input": { - "ipAddressOrRange": "[coalesce(tryGet(parameters('networkRestrictions'), 'ipRules'), createArray())[copyIndex('ipRules')]]" - } - }, - { - "name": "virtualNetworkRules", - "count": "[length(coalesce(tryGet(parameters('networkRestrictions'), 'virtualNetworkRules'), createArray()))]", - "input": { - "id": "[coalesce(tryGet(parameters('networkRestrictions'), 'virtualNetworkRules'), createArray())[copyIndex('virtualNetworkRules')].subnetResourceId]", - "ignoreMissingVnetServiceEndpoint": false - } - }, - { - "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)))))]" - } - ], - "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", - "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned,UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', null())), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", - "consistencyPolicy": { - "Eventual": { - "defaultConsistencyLevel": "Eventual" - }, - "ConsistentPrefix": { - "defaultConsistencyLevel": "ConsistentPrefix" - }, - "Session": { - "defaultConsistencyLevel": "Session" - }, - "BoundedStaleness": { - "defaultConsistencyLevel": "BoundedStaleness", - "maxStalenessPrefix": "[parameters('maxStalenessPrefix')]", - "maxIntervalInSeconds": "[parameters('maxIntervalInSeconds')]" - }, - "Strong": { - "defaultConsistencyLevel": "Strong" - } - }, - "defaultFailoverLocation": [ - { - "failoverPriority": 0, - "locationName": "[parameters('location')]", - "isZoneRedundant": true - } - ], - "kind": "[if(or(not(empty(parameters('sqlDatabases'))), not(empty(parameters('gremlinDatabases')))), 'GlobalDocumentDB', if(not(empty(parameters('mongodbDatabases'))), 'MongoDB', 'GlobalDocumentDB'))]", - "backupPolicy": "[if(equals(parameters('backupPolicyType'), 'Continuous'), createObject('type', parameters('backupPolicyType'), 'continuousModeProperties', createObject('tier', parameters('backupPolicyContinuousTier'))), createObject('type', parameters('backupPolicyType'), 'periodicModeProperties', createObject('backupIntervalInMinutes', parameters('backupIntervalInMinutes'), 'backupRetentionIntervalInHours', parameters('backupRetentionIntervalInHours'), 'backupStorageRedundancy', parameters('backupStorageRedundancy'))))]", - "databaseAccountProperties": "[union(createObject('databaseAccountOfferType', parameters('databaseAccountOfferType'), 'backupPolicy', variables('backupPolicy'), 'capabilities', variables('capabilities'), 'minimalTlsVersion', parameters('minimumTlsVersion'), 'capacity', createObject('totalThroughputLimit', parameters('totalThroughputLimit'))), if(or(or(or(not(empty(parameters('sqlDatabases'))), not(empty(parameters('mongodbDatabases')))), not(empty(parameters('gremlinDatabases')))), not(empty(parameters('tables')))), createObject('consistencyPolicy', variables('consistencyPolicy')[parameters('defaultConsistencyLevel')], 'enableMultipleWriteLocations', parameters('enableMultipleWriteLocations'), 'locations', if(empty(variables('databaseAccount_locations')), variables('defaultFailoverLocation'), variables('databaseAccount_locations')), 'ipRules', variables('ipRules'), 'virtualNetworkRules', variables('virtualNetworkRules'), 'networkAclBypass', coalesce(tryGet(parameters('networkRestrictions'), 'networkAclBypass'), 'None'), 'publicNetworkAccess', coalesce(tryGet(parameters('networkRestrictions'), 'publicNetworkAccess'), 'Disabled'), 'isVirtualNetworkFilterEnabled', or(not(empty(variables('ipRules'))), not(empty(variables('virtualNetworkRules')))), 'enableFreeTier', parameters('enableFreeTier'), 'enableAutomaticFailover', parameters('automaticFailover'), 'enableAnalyticalStorage', parameters('enableAnalyticalStorage')), createObject()), if(or(not(empty(parameters('sqlDatabases'))), not(empty(parameters('tables')))), createObject('disableLocalAuth', parameters('disableLocalAuth'), 'disableKeyBasedMetadataWriteAccess', parameters('disableKeyBasedMetadataWriteAccess')), createObject()), if(not(empty(parameters('mongodbDatabases'))), createObject('apiProperties', createObject('serverVersion', parameters('serverVersion'))), createObject()))]", - "builtInRoleNames": { - "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", - "Cosmos DB Account Reader Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fbdf93bf-df7d-467e-a4d2-9458aa1360c8')]", - "Cosmos DB Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa')]", - "CosmosBackupOperator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'db7b14f2-5adf-42da-9f96-f2ee17bab5cb')]", - "CosmosRestoreOperator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5432c526-bc82-444a-b7ba-57c5b0b5b34f')]", - "DocumentDB Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5bd9cd88-fe45-4216-938b-f97437e15450')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" - } - }, - "resources": { - "avmTelemetry": { - "condition": "[parameters('enableTelemetry')]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2024-03-01", - "name": "[format('46d3xbcp.res.documentdb-databaseaccount.{0}.{1}', replace('0.11.0', '.', '-'), 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" - } + }, + "restorableDroppedDatabaseResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource identifier of the restorable dropped database associated with create operation of this database." + } + }, + "restorePointInTime": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specifies the point in time (ISO8601 format) of the source database that will be restored to create the new database." } - } - } - }, - "databaseAccount": { - "type": "Microsoft.DocumentDB/databaseAccounts", - "apiVersion": "2023-04-15", - "name": "[parameters('name')]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", - "identity": "[variables('identity')]", - "kind": "[variables('kind')]", - "properties": "[variables('databaseAccountProperties')]" - }, - "databaseAccount_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.DocumentDB/databaseAccounts/{0}', parameters('name'))]", - "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", - "properties": { - "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", - "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" - }, - "dependsOn": [ - "databaseAccount" - ] - }, - "databaseAccount_diagnosticSettings": { - "copy": { - "name": "databaseAccount_diagnosticSettings", - "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" - }, - "type": "Microsoft.Insights/diagnosticSettings", - "apiVersion": "2021-05-01-preview", - "scope": "[format('Microsoft.DocumentDB/databaseAccounts/{0}', parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", - "properties": { - "copy": [ - { - "name": "metrics", - "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", - "input": { - "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", - "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", - "timeGrain": null - } - }, - { - "name": "logs", - "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", - "input": { - "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", - "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", - "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" - } + }, + "sampleName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the sample schema to apply when creating this database." } - ], - "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", - "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", - "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", - "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", - "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", - "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" - }, - "dependsOn": [ - "databaseAccount" - ] - }, - "databaseAccount_roleAssignments": { - "copy": { - "name": "databaseAccount_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.DocumentDB/databaseAccounts/{0}', parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.DocumentDB/databaseAccounts', 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": [ - "databaseAccount" - ] - }, - "databaseAccount_sqlDatabases": { - "copy": { - "name": "databaseAccount_sqlDatabases", - "count": "[length(parameters('sqlDatabases'))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-sqldb-{1}', uniqueString(deployment().name, parameters('location')), parameters('sqlDatabases')[copyIndex()].name)]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[parameters('sqlDatabases')[copyIndex()].name]" - }, - "containers": { - "value": "[tryGet(parameters('sqlDatabases')[copyIndex()], 'containers')]" - }, - "throughput": { - "value": "[tryGet(parameters('sqlDatabases')[copyIndex()], 'throughput')]" - }, - "databaseAccountName": { - "value": "[parameters('name')]" - }, - "autoscaleSettingsMaxThroughput": { - "value": "[tryGet(parameters('sqlDatabases')[copyIndex()], 'autoscaleSettingsMaxThroughput')]" + "secondaryType": { + "type": "string", + "allowedValues": [ + "Geo", + "Named", + "Standby" + ], + "nullable": true, + "metadata": { + "description": "Optional. The secondary type of the database if it is a secondary." } }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "languageVersion": "2.0", - "contentVersion": "1.0.0.0", + "sourceDatabaseDeletionDate": { + "type": "string", + "nullable": true, "metadata": { - "_generator": { - "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "1989869202015976298" - }, - "name": "DocumentDB Database Account SQL Databases", - "description": "This module deploys a SQL Database in a CosmosDB Account." - }, - "parameters": { - "databaseAccountName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent Database Account. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", - "metadata": { - "description": "Required. Name of the SQL database ." - } - }, - "containers": { - "type": "array", - "items": { - "type": "object" - }, - "defaultValue": [], - "metadata": { - "description": "Optional. Array of containers to deploy in the SQL database." - } - }, - "throughput": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. Request units per second. Will be ignored if autoscaleSettingsMaxThroughput is used. Setting throughput at the database level is only recommended for development/test or when workload across all containers in the shared throughput database is uniform. For best performance for large production workloads, it is recommended to set dedicated throughput (autoscale or manual) at the container level and not at the database level." - } - }, - "autoscaleSettingsMaxThroughput": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. Specifies the Autoscale settings and represents maximum throughput, the resource can scale up to. The autoscale throughput should have valid throughput values between 1000 and 1000000 inclusive in increments of 1000. If value is set to null, then autoscale will be disabled. Setting throughput at the database level is only recommended for development/test or when workload across all containers in the shared throughput database is uniform. For best performance for large production workloads, it is recommended to set dedicated throughput (autoscale or manual) at the container level and not at the database level." - } - }, - "tags": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Tags of the SQL database resource." - } - } - }, - "resources": { - "databaseAccount": { - "existing": true, - "type": "Microsoft.DocumentDB/databaseAccounts", - "apiVersion": "2023-04-15", - "name": "[parameters('databaseAccountName')]" - }, - "sqlDatabase": { - "type": "Microsoft.DocumentDB/databaseAccounts/sqlDatabases", - "apiVersion": "2023-04-15", - "name": "[format('{0}/{1}', parameters('databaseAccountName'), parameters('name'))]", - "tags": "[parameters('tags')]", - "properties": { - "resource": { - "id": "[parameters('name')]" - }, - "options": "[if(contains(reference('databaseAccount').capabilities, createObject('name', 'EnableServerless')), null(), createObject('throughput', if(equals(parameters('autoscaleSettingsMaxThroughput'), null()), parameters('throughput'), null()), 'autoscaleSettings', if(not(equals(parameters('autoscaleSettingsMaxThroughput'), null())), createObject('maxThroughput', parameters('autoscaleSettingsMaxThroughput')), null())))]" - }, - "dependsOn": [ - "databaseAccount" - ] - }, - "container": { - "copy": { - "name": "container", - "count": "[length(parameters('containers'))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-sqldb-{1}', uniqueString(deployment().name, parameters('name')), parameters('containers')[copyIndex()].name)]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "databaseAccountName": { - "value": "[parameters('databaseAccountName')]" - }, - "sqlDatabaseName": { - "value": "[parameters('name')]" - }, - "name": { - "value": "[parameters('containers')[copyIndex()].name]" - }, - "analyticalStorageTtl": { - "value": "[tryGet(parameters('containers')[copyIndex()], 'analyticalStorageTtl')]" - }, - "autoscaleSettingsMaxThroughput": { - "value": "[tryGet(parameters('containers')[copyIndex()], 'autoscaleSettingsMaxThroughput')]" - }, - "conflictResolutionPolicy": { - "value": "[tryGet(parameters('containers')[copyIndex()], 'conflictResolutionPolicy')]" - }, - "defaultTtl": { - "value": "[tryGet(parameters('containers')[copyIndex()], 'defaultTtl')]" - }, - "indexingPolicy": { - "value": "[tryGet(parameters('containers')[copyIndex()], 'indexingPolicy')]" - }, - "kind": { - "value": "[tryGet(parameters('containers')[copyIndex()], 'kind')]" - }, - "version": { - "value": "[tryGet(parameters('containers')[copyIndex()], 'version')]" - }, - "paths": { - "value": "[tryGet(parameters('containers')[copyIndex()], 'paths')]" - }, - "throughput": "[if(and(or(not(equals(parameters('throughput'), null())), not(equals(parameters('autoscaleSettingsMaxThroughput'), null()))), equals(tryGet(parameters('containers')[copyIndex()], 'throughput'), null())), createObject('value', -1), createObject('value', tryGet(parameters('containers')[copyIndex()], 'throughput')))]", - "uniqueKeyPolicyKeys": { - "value": "[tryGet(parameters('containers')[copyIndex()], 'uniqueKeyPolicyKeys')]" - } - }, - "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.33.93.31351", - "templateHash": "12023419088114367985" - }, - "name": "DocumentDB Database Account SQL Database Containers", - "description": "This module deploys a SQL Database Container in a CosmosDB Account." - }, - "parameters": { - "databaseAccountName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent Database Account. Required if the template is used in a standalone deployment." - } - }, - "sqlDatabaseName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent SQL Database. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", - "metadata": { - "description": "Required. Name of the container." - } - }, - "analyticalStorageTtl": { - "type": "int", - "defaultValue": 0, - "metadata": { - "description": "Optional. Default to 0. Indicates how long data should be retained in the analytical store, for a container. Analytical store is enabled when ATTL is set with a value other than 0. If the value is set to -1, the analytical store retains all historical data, irrespective of the retention of the data in the transactional store." - } - }, - "conflictResolutionPolicy": { - "type": "object", - "defaultValue": {}, - "metadata": { - "description": "Optional. The conflict resolution policy for the container. Conflicts and conflict resolution policies are applicable if the Azure Cosmos DB account is configured with multiple write regions." - } - }, - "defaultTtl": { - "type": "int", - "defaultValue": -1, - "minValue": -1, - "maxValue": 2147483647, - "metadata": { - "description": "Optional. Default to -1. Default time to live (in seconds). With Time to Live or TTL, Azure Cosmos DB provides the ability to delete items automatically from a container after a certain time period. If the value is set to \"-1\", it is equal to infinity, and items don't expire by default." - } - }, - "throughput": { - "type": "int", - "defaultValue": 400, - "metadata": { - "description": "Optional. Default to 400. Request Units per second. Will be ignored if autoscaleSettingsMaxThroughput is used. For best performance for large production workloads, it is recommended to set dedicated throughput (autoscale or manual) at the container level and not at the database level." - } - }, - "autoscaleSettingsMaxThroughput": { - "type": "int", - "nullable": true, - "maxValue": 1000000, - "metadata": { - "description": "Optional. Specifies the Autoscale settings and represents maximum throughput, the resource can scale up to. The autoscale throughput should have valid throughput values between 1000 and 1000000 inclusive in increments of 1000. If value is set to null, then autoscale will be disabled. For best performance for large production workloads, it is recommended to set dedicated throughput (autoscale or manual) at the container level and not at the database level." - } - }, - "tags": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Tags of the SQL Database resource." - } - }, - "paths": { - "type": "array", - "items": { - "type": "string" - }, - "minLength": 1, - "maxLength": 3, - "metadata": { - "description": "Required. List of paths using which data within the container can be partitioned. For kind=MultiHash it can be up to 3. For anything else it needs to be exactly 1." - } - }, - "indexingPolicy": { - "type": "object", - "defaultValue": {}, - "metadata": { - "description": "Optional. Indexing policy of the container." - } - }, - "uniqueKeyPolicyKeys": { - "type": "array", - "defaultValue": [], - "metadata": { - "description": "Optional. The unique key policy configuration containing a list of unique keys that enforces uniqueness constraint on documents in the collection in the Azure Cosmos DB service." - } - }, - "kind": { - "type": "string", - "defaultValue": "Hash", - "allowedValues": [ - "Hash", - "MultiHash" - ], - "metadata": { - "description": "Optional. Default to Hash. Indicates the kind of algorithm used for partitioning." - } - }, - "version": { - "type": "int", - "defaultValue": 1, - "allowedValues": [ - 1, - 2 - ], - "metadata": { - "description": "Optional. Default to 1 for Hash and 2 for MultiHash - 1 is not allowed for MultiHash. Version of the partition key definition." - } - } - }, - "variables": { - "copy": [ - { - "name": "partitionKeyPaths", - "count": "[length(parameters('paths'))]", - "input": "[if(startsWith(parameters('paths')[copyIndex('partitionKeyPaths')], '/'), parameters('paths')[copyIndex('partitionKeyPaths')], format('/{0}', parameters('paths')[copyIndex('partitionKeyPaths')]))]" - } - ], - "containerResourceParams": "[union(createObject('conflictResolutionPolicy', parameters('conflictResolutionPolicy'), 'defaultTtl', parameters('defaultTtl'), 'id', parameters('name'), 'indexingPolicy', if(not(empty(parameters('indexingPolicy'))), parameters('indexingPolicy'), null()), 'partitionKey', createObject('paths', variables('partitionKeyPaths'), 'kind', parameters('kind'), 'version', if(equals(parameters('kind'), 'MultiHash'), 2, parameters('version'))), 'uniqueKeyPolicy', if(not(empty(parameters('uniqueKeyPolicyKeys'))), createObject('uniqueKeys', parameters('uniqueKeyPolicyKeys')), null())), if(not(equals(parameters('analyticalStorageTtl'), 0)), createObject('analyticalStorageTtl', parameters('analyticalStorageTtl')), createObject()))]" - }, - "resources": { - "databaseAccount::sqlDatabase": { - "existing": true, - "type": "Microsoft.DocumentDB/databaseAccounts/sqlDatabases", - "apiVersion": "2023-04-15", - "name": "[format('{0}/{1}', parameters('databaseAccountName'), parameters('sqlDatabaseName'))]" - }, - "databaseAccount": { - "existing": true, - "type": "Microsoft.DocumentDB/databaseAccounts", - "apiVersion": "2023-04-15", - "name": "[parameters('databaseAccountName')]" - }, - "container": { - "type": "Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers", - "apiVersion": "2023-04-15", - "name": "[format('{0}/{1}/{2}', parameters('databaseAccountName'), parameters('sqlDatabaseName'), parameters('name'))]", - "tags": "[parameters('tags')]", - "properties": { - "resource": "[variables('containerResourceParams')]", - "options": "[if(contains(reference('databaseAccount').capabilities, createObject('name', 'EnableServerless')), null(), createObject('throughput', if(and(equals(parameters('autoscaleSettingsMaxThroughput'), null()), not(equals(parameters('throughput'), -1))), parameters('throughput'), null()), 'autoscaleSettings', if(not(equals(parameters('autoscaleSettingsMaxThroughput'), null())), createObject('maxThroughput', parameters('autoscaleSettingsMaxThroughput')), null())))]" - }, - "dependsOn": [ - "databaseAccount" - ] - } - }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the container." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the container." - }, - "value": "[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers', parameters('databaseAccountName'), parameters('sqlDatabaseName'), parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The name of the resource group the container was created in." - }, - "value": "[resourceGroup().name]" - } - } - } - }, - "dependsOn": [ - "sqlDatabase" - ] - } + "description": "Optional. Specifies the time that the database was deleted." + } + }, + "sourceDatabaseResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource identifier of the source database associated with create operation of this database." + } + }, + "sourceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource identifier of the source associated with the create operation of this database." + } + }, + "useFreeLimit": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Whether or not the database uses free monthly limits. Allowed on one database in a subscription." + } + }, + "zoneRedundant": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Whether or not this database is zone redundant, which means the replicas of this database will be spread across multiple availability zones." + } + }, + "diagnosticSettings": { + "type": "array", + "items": { + "$ref": "#/definitions/diagnosticSettingFullType" }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the SQL database." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the SQL database." - }, - "value": "[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlDatabases', parameters('databaseAccountName'), parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The name of the resource group the SQL database was created in." - }, - "value": "[resourceGroup().name]" - } + "nullable": true, + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "backupShortTermRetentionPolicy": { + "$ref": "#/definitions/shortTermBackupRetentionPolicyType", + "nullable": true, + "metadata": { + "description": "Optional. The short term backup retention policy for the database." + } + }, + "backupLongTermRetentionPolicy": { + "$ref": "#/definitions/longTermBackupRetentionPolicyType", + "nullable": true, + "metadata": { + "description": "Optional. The long term backup retention policy for the database." } } }, - "dependsOn": [ - "databaseAccount" - ] + "metadata": { + "__bicep_export!": true + } }, - "databaseAccount_sqlRoleDefinitions": { - "copy": { - "name": "databaseAccount_sqlRoleDefinitions", - "count": "[length(coalesce(parameters('sqlRoleDefinitions'), createArray()))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-sqlrd-{1}', uniqueString(deployment().name, parameters('location')), coalesce(parameters('sqlRoleDefinitions'), createArray())[copyIndex()].name)]", + "elasticPoolPropertyType": { + "type": "object", "properties": { - "expressionEvaluationOptions": { - "scope": "inner" + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the Elastic Pool." + } }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[coalesce(parameters('sqlRoleDefinitions'), createArray())[copyIndex()].name]" - }, - "databaseAccountName": { - "value": "[parameters('name')]" - }, - "dataActions": { - "value": "[tryGet(coalesce(parameters('sqlRoleDefinitions'), createArray())[copyIndex()], 'dataActions')]" - }, - "roleName": { - "value": "[tryGet(coalesce(parameters('sqlRoleDefinitions'), createArray())[copyIndex()], 'roleName')]" - }, - "roleType": { - "value": "[tryGet(coalesce(parameters('sqlRoleDefinitions'), createArray())[copyIndex()], 'roleType')]" - }, - "principalIds": { - "value": "[parameters('sqlRoleAssignmentsPrincipalIds')]" + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." } }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", + "sku": { + "$ref": "#/definitions/elasticPoolSkuType", + "nullable": true, "metadata": { - "_generator": { - "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "10702362485253818591" - }, - "name": "DocumentDB Database Account SQL Role.", - "description": "This module deploys SQL Role Definision and Assignment in a CosmosDB Account." - }, - "parameters": { - "databaseAccountName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent Database Account. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", - "metadata": { - "description": "Required. Name of the SQL Role." - } - }, - "dataActions": { - "type": "array", - "defaultValue": [ - "Microsoft.DocumentDB/databaseAccounts/readMetadata", - "Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/items/*", - "Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/*" - ], - "metadata": { - "description": "Optional. An array of data actions that are allowed." - } - }, - "principalIds": { - "type": "array", - "defaultValue": [], - "metadata": { - "description": "Optional. Ids needs to be granted." - } - }, - "roleName": { - "type": "string", - "defaultValue": "Reader Writer", - "metadata": { - "description": "Optional. A user-friendly name for the Role Definition. Must be unique for the database account." - } - }, - "roleType": { - "type": "string", - "defaultValue": "CustomRole", - "allowedValues": [ - "CustomRole", - "BuiltInRole" - ], - "metadata": { - "description": "Optional. Indicates whether the Role Definition was built-in or user created." - } - } - }, - "resources": [ - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('sql-role-definition-{0}', uniqueString(parameters('name')))]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "databaseAccountName": { - "value": "[parameters('databaseAccountName')]" - }, - "dataActions": { - "value": "[parameters('dataActions')]" - }, - "roleName": { - "value": "[parameters('roleName')]" - }, - "roleType": { - "value": "[parameters('roleType')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "226082505863315224" - }, - "name": "DocumentDB Database Account SQL Role Definitions.", - "description": "This module deploys a SQL Role Definision in a CosmosDB Account." - }, - "parameters": { - "databaseAccountName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent Database Account. Required if the template is used in a standalone deployment." - } - }, - "dataActions": { - "type": "array", - "defaultValue": [], - "metadata": { - "description": "Optional. An array of data actions that are allowed." - } - }, - "roleName": { - "type": "string", - "defaultValue": "Reader Writer", - "metadata": { - "description": "Optional. A user-friendly name for the Role Definition. Must be unique for the database account." - } - }, - "roleType": { - "type": "string", - "defaultValue": "CustomRole", - "allowedValues": [ - "CustomRole", - "BuiltInRole" - ], - "metadata": { - "description": "Optional. Indicates whether the Role Definition was built-in or user created." - } - } - }, - "resources": [ - { - "type": "Microsoft.DocumentDB/databaseAccounts/sqlRoleDefinitions", - "apiVersion": "2023-04-15", - "name": "[format('{0}/{1}', parameters('databaseAccountName'), guid(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName')), parameters('databaseAccountName'), 'sql-role'))]", - "properties": { - "assignableScopes": [ - "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName'))]" - ], - "permissions": [ - { - "dataActions": "[parameters('dataActions')]" - } - ], - "roleName": "[parameters('roleName')]", - "type": "[parameters('roleType')]" - } - } - ], - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the SQL database." - }, - "value": "[guid(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName')), parameters('databaseAccountName'), 'sql-role')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the SQL database." - }, - "value": "[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlRoleDefinitions', parameters('databaseAccountName'), guid(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName')), parameters('databaseAccountName'), 'sql-role'))]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The name of the resource group the SQL database was created in." - }, - "value": "[resourceGroup().name]" - } - } - } - } - }, - { - "copy": { - "name": "sqlRoleAssignment", - "count": "[length(parameters('principalIds'))]", - "mode": "serial", - "batchSize": 1 - }, - "condition": "[not(empty(parameters('principalIds')[copyIndex()]))]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('sql-role-assign-{0}', uniqueString(parameters('principalIds')[copyIndex()]))]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[guid(reference(resourceId('Microsoft.Resources/deployments', format('sql-role-definition-{0}', uniqueString(parameters('name')))), '2022-09-01').outputs.resourceId.value, parameters('principalIds')[copyIndex()], resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName')))]" - }, - "databaseAccountName": { - "value": "[parameters('databaseAccountName')]" - }, - "roleDefinitionId": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('sql-role-definition-{0}', uniqueString(parameters('name')))), '2022-09-01').outputs.resourceId.value]" - }, - "principalId": { - "value": "[parameters('principalIds')[copyIndex()]]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "4459562534723287578" - }, - "name": "DocumentDB Database Account SQL Role Assignments.", - "description": "This module deploys a SQL Role Assignment in a CosmosDB Account." - }, - "parameters": { - "databaseAccountName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent Database Account. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", - "metadata": { - "description": "Required. Name of the SQL Role Assignment." - } - }, - "principalId": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Optional. Id needs to be granted." - } - }, - "roleDefinitionId": { - "type": "string", - "metadata": { - "description": "Required. Id of the SQL Role Definition." - } - } - }, - "resources": [ - { - "type": "Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments", - "apiVersion": "2023-04-15", - "name": "[format('{0}/{1}', parameters('databaseAccountName'), parameters('name'))]", - "properties": { - "principalId": "[parameters('principalId')]", - "roleDefinitionId": "[parameters('roleDefinitionId')]", - "scope": "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName'))]" - } - } - ], - "outputs": { - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The name of the resource group the SQL Role Assignment was created in." - }, - "value": "[resourceGroup().name]" - } - } - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', format('sql-role-definition-{0}', uniqueString(parameters('name'))))]" - ] - } + "description": "Optional. The elastic pool SKU." + } + }, + "autoPauseDelay": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Time in minutes after which elastic pool is automatically paused. A value of -1 means that automatic pause is disabled." + } + }, + "availabilityZone": { + "type": "string", + "allowedValues": [ + "1", + "2", + "3", + "NoPreference" ], - "outputs": { - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The name of the resource group the SQL Role Definition and Assignment were created in." - }, - "value": "[resourceGroup().name]" - } + "nullable": true, + "metadata": { + "description": "Optional. Specifies the availability zone the pool's primary replica is pinned to." + } + }, + "highAvailabilityReplicaCount": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The number of secondary replicas associated with the elastic pool that are used to provide high availability. Applicable only to Hyperscale elastic pools." + } + }, + "licenseType": { + "type": "string", + "allowedValues": [ + "BasePrice", + "LicenseIncluded" + ], + "nullable": true, + "metadata": { + "description": "Optional. The license type to apply for this elastic pool." + } + }, + "maintenanceConfigurationId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Maintenance configuration id assigned to the elastic pool. This configuration defines the period when the maintenance updates will will occur." + } + }, + "maxSizeBytes": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The storage limit for the database elastic pool in bytes." + } + }, + "minCapacity": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Minimal capacity that serverless pool will not shrink below, if not paused." + } + }, + "perDatabaseSettings": { + "$ref": "#/definitions/elasticPoolPerDatabaseSettingsType", + "nullable": true, + "metadata": { + "description": "Optional. The per database settings for the elastic pool." + } + }, + "preferredEnclaveType": { + "type": "string", + "allowedValues": [ + "Default", + "VBS" + ], + "nullable": true, + "metadata": { + "description": "Optional. Type of enclave requested on the elastic pool." + } + }, + "zoneRedundant": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Whether or not this elastic pool is zone redundant, which means the replicas of this elastic pool will be spread across multiple availability zones." } } }, - "dependsOn": [ - "databaseAccount" - ] + "metadata": { + "__bicep_export!": true + } }, - "databaseAccount_mongodbDatabases": { - "copy": { - "name": "databaseAccount_mongodbDatabases", - "count": "[length(parameters('mongodbDatabases'))]" + "encryptionProtectorType": { + "type": "object", + "properties": { + "serverKeyName": { + "type": "string", + "metadata": { + "description": "Required. The name of the server key." + } + }, + "serverKeyType": { + "type": "string", + "allowedValues": [ + "AzureKeyVault", + "ServiceManaged" + ], + "nullable": true, + "metadata": { + "description": "Optional. The encryption protector type." + } + }, + "autoRotationEnabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Key auto rotation opt-in flag." + } + } }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-mongodb-{1}', uniqueString(deployment().name, parameters('location')), parameters('mongodbDatabases')[copyIndex()].name)]", + "metadata": { + "__bicep_export!": true + } + }, + "vulnerabilityAssessmentType": { + "type": "object", "properties": { - "expressionEvaluationOptions": { - "scope": "inner" + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the vulnerability assessment." + } }, - "mode": "Incremental", - "parameters": { - "databaseAccountName": { - "value": "[parameters('name')]" - }, - "name": { - "value": "[parameters('mongodbDatabases')[copyIndex()].name]" - }, - "tags": { - "value": "[coalesce(tryGet(parameters('mongodbDatabases')[copyIndex()], 'tags'), parameters('tags'))]" - }, - "collections": { - "value": "[tryGet(parameters('mongodbDatabases')[copyIndex()], 'collections')]" - }, - "throughput": { - "value": "[tryGet(parameters('mongodbDatabases')[copyIndex()], 'throughput')]" + "recurringScans": { + "$ref": "#/definitions/recurringScansType", + "nullable": true, + "metadata": { + "description": "Optional. The recurring scans settings." + } + }, + "storageAccountResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the storage account to store the scan reports." + } + }, + "useStorageAccountAccessKey": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Specifies whether to use the storage account access key to access the storage account." } }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "languageVersion": "2.0", - "contentVersion": "1.0.0.0", + "createStorageRoleAssignment": { + "type": "bool", + "nullable": true, "metadata": { - "_generator": { - "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "8920471273670088909" - }, - "name": "DocumentDB Database Account MongoDB Databases", - "description": "This module deploys a MongoDB Database within a CosmosDB Account." - }, - "parameters": { - "databaseAccountName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent Cosmos DB database account. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", - "metadata": { - "description": "Required. Name of the mongodb database." - } - }, - "throughput": { - "type": "int", - "defaultValue": 400, - "metadata": { - "description": "Optional. Request Units per second. Setting throughput at the database level is only recommended for development/test or when workload across all collections in the shared throughput database is uniform. For best performance for large production workloads, it is recommended to set dedicated throughput (autoscale or manual) at the collection level and not at the database level." - } - }, - "collections": { - "type": "array", - "defaultValue": [], - "metadata": { - "description": "Optional. Collections in the mongodb database." - } - }, - "tags": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Tags of the resource." - } - } - }, - "resources": { - "databaseAccount": { - "existing": true, - "type": "Microsoft.DocumentDB/databaseAccounts", - "apiVersion": "2023-04-15", - "name": "[parameters('databaseAccountName')]" - }, - "mongodbDatabase": { - "type": "Microsoft.DocumentDB/databaseAccounts/mongodbDatabases", - "apiVersion": "2023-04-15", - "name": "[format('{0}/{1}', parameters('databaseAccountName'), parameters('name'))]", - "tags": "[parameters('tags')]", - "properties": { - "resource": { - "id": "[parameters('name')]" - }, - "options": "[if(contains(reference('databaseAccount').capabilities, createObject('name', 'EnableServerless')), null(), createObject('throughput', parameters('throughput')))]" - }, - "dependsOn": [ - "databaseAccount" - ] - }, - "mongodbDatabase_collections": { - "copy": { - "name": "mongodbDatabase_collections", - "count": "[length(parameters('collections'))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-collection-{1}', uniqueString(deployment().name, parameters('name')), parameters('collections')[copyIndex()].name)]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "databaseAccountName": { - "value": "[parameters('databaseAccountName')]" - }, - "mongodbDatabaseName": { - "value": "[parameters('name')]" - }, - "name": { - "value": "[parameters('collections')[copyIndex()].name]" - }, - "indexes": { - "value": "[parameters('collections')[copyIndex()].indexes]" - }, - "shardKey": { - "value": "[parameters('collections')[copyIndex()].shardKey]" - }, - "throughput": { - "value": "[tryGet(parameters('collections')[copyIndex()], 'throughput')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "7171237014304841298" - }, - "name": "DocumentDB Database Account MongoDB Database Collections", - "description": "This module deploys a MongoDB Database Collection." - }, - "parameters": { - "databaseAccountName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent Cosmos DB database account. Required if the template is used in a standalone deployment." - } - }, - "mongodbDatabaseName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent mongodb database. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", - "metadata": { - "description": "Required. Name of the collection." - } - }, - "throughput": { - "type": "int", - "defaultValue": 400, - "metadata": { - "description": "Optional. Request Units per second. For best performance for large production workloads, it is recommended to set dedicated throughput (autoscale or manual) at the collection level and not at the database level." - } - }, - "indexes": { - "type": "array", - "metadata": { - "description": "Required. Indexes for the collection." - } - }, - "shardKey": { - "type": "object", - "metadata": { - "description": "Required. ShardKey for the collection." - } - } - }, - "resources": [ - { - "type": "Microsoft.DocumentDB/databaseAccounts/mongodbDatabases/collections", - "apiVersion": "2023-04-15", - "name": "[format('{0}/{1}/{2}', parameters('databaseAccountName'), parameters('mongodbDatabaseName'), parameters('name'))]", - "properties": { - "options": "[if(contains(reference(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName')), '2023-04-15').capabilities, createObject('name', 'EnableServerless')), null(), createObject('throughput', parameters('throughput')))]", - "resource": { - "id": "[parameters('name')]", - "indexes": "[parameters('indexes')]", - "shardKey": "[parameters('shardKey')]" - } - } - } - ], - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the mongodb database collection." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the mongodb database collection." - }, - "value": "[resourceId('Microsoft.DocumentDB/databaseAccounts/mongodbDatabases/collections', parameters('databaseAccountName'), parameters('mongodbDatabaseName'), parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The name of the resource group the mongodb database collection was created in." - }, - "value": "[resourceGroup().name]" - } - } - } - }, - "dependsOn": [ - "mongodbDatabase" - ] - } - }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the mongodb database." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the mongodb database." - }, - "value": "[resourceId('Microsoft.DocumentDB/databaseAccounts/mongodbDatabases', parameters('databaseAccountName'), parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The name of the resource group the mongodb database was created in." - }, - "value": "[resourceGroup().name]" - } + "description": "Optional. Specifies whether to create a role assignment for the storage account." } } }, - "dependsOn": [ - "databaseAccount" - ] + "metadata": { + "__bicep_export!": true + } }, - "databaseAccount_gremlinDatabases": { - "copy": { - "name": "databaseAccount_gremlinDatabases", - "count": "[length(parameters('gremlinDatabases'))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-gremlin-{1}', uniqueString(deployment().name, parameters('location')), parameters('gremlinDatabases')[copyIndex()].name)]", + "firewallRuleType": { + "type": "object", "properties": { - "expressionEvaluationOptions": { - "scope": "inner" + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the firewall rule." + } }, - "mode": "Incremental", - "parameters": { - "databaseAccountName": { - "value": "[parameters('name')]" - }, - "name": { - "value": "[parameters('gremlinDatabases')[copyIndex()].name]" - }, - "tags": { - "value": "[coalesce(tryGet(parameters('gremlinDatabases')[copyIndex()], 'tags'), parameters('tags'))]" - }, - "graphs": { - "value": "[tryGet(parameters('gremlinDatabases')[copyIndex()], 'graphs')]" - }, - "maxThroughput": { - "value": "[tryGet(parameters('gremlinDatabases')[copyIndex()], 'maxThroughput')]" - }, - "throughput": { - "value": "[tryGet(parameters('gremlinDatabases')[copyIndex()], 'throughput')]" + "startIpAddress": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The start IP address of the firewall rule. Must be IPv4 format. Use value '0.0.0.0' for all Azure-internal IP addresses." } }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "languageVersion": "2.0", - "contentVersion": "1.0.0.0", + "endIpAddress": { + "type": "string", + "nullable": true, "metadata": { - "_generator": { - "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "2305698409425699794" - }, - "name": "DocumentDB Database Account Gremlin Databases", - "description": "This module deploys a Gremlin Database within a CosmosDB Account." - }, - "parameters": { - "name": { - "type": "string", - "metadata": { - "description": "Required. Name of the Gremlin database." - } - }, - "tags": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Tags of the Gremlin database resource." - } - }, - "databaseAccountName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent Gremlin database. Required if the template is used in a standalone deployment." - } - }, - "graphs": { - "type": "array", - "defaultValue": [], - "metadata": { - "description": "Optional. Array of graphs to deploy in the Gremlin database." - } - }, - "maxThroughput": { - "type": "int", - "defaultValue": 4000, - "metadata": { - "description": "Optional. Represents maximum throughput, the resource can scale up to. Cannot be set together with `throughput`. If `throughput` is set to something else than -1, this autoscale setting is ignored. Setting throughput at the database level is only recommended for development/test or when workload across all graphs in the shared throughput database is uniform. For best performance for large production workloads, it is recommended to set dedicated throughput (autoscale or manual) at the graph level and not at the database level." - } - }, - "throughput": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. Request Units per second (for example 10000). Cannot be set together with `maxThroughput`. Setting throughput at the database level is only recommended for development/test or when workload across all graphs in the shared throughput database is uniform. For best performance for large production workloads, it is recommended to set dedicated throughput (autoscale or manual) at the graph level and not at the database level." - } - } - }, - "resources": { - "databaseAccount": { - "existing": true, - "type": "Microsoft.DocumentDB/databaseAccounts", - "apiVersion": "2023-04-15", - "name": "[parameters('databaseAccountName')]" - }, - "gremlinDatabase": { - "type": "Microsoft.DocumentDB/databaseAccounts/gremlinDatabases", - "apiVersion": "2023-04-15", - "name": "[format('{0}/{1}', parameters('databaseAccountName'), parameters('name'))]", - "tags": "[parameters('tags')]", - "properties": { - "options": "[if(contains(reference('databaseAccount').capabilities, createObject('name', 'EnableServerless')), createObject(), createObject('autoscaleSettings', if(equals(parameters('throughput'), null()), createObject('maxThroughput', parameters('maxThroughput')), null()), 'throughput', parameters('throughput')))]", - "resource": { - "id": "[parameters('name')]" - } - }, - "dependsOn": [ - "databaseAccount" - ] - }, - "gremlinDatabase_gremlinGraphs": { - "copy": { - "name": "gremlinDatabase_gremlinGraphs", - "count": "[length(parameters('graphs'))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-gremlindb-{1}', uniqueString(deployment().name, parameters('name')), parameters('graphs')[copyIndex()].name)]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[parameters('graphs')[copyIndex()].name]" - }, - "gremlinDatabaseName": { - "value": "[parameters('name')]" - }, - "databaseAccountName": { - "value": "[parameters('databaseAccountName')]" - }, - "indexingPolicy": { - "value": "[tryGet(parameters('graphs')[copyIndex()], 'indexingPolicy')]" - }, - "partitionKeyPaths": "[if(not(empty(parameters('graphs')[copyIndex()].partitionKeyPaths)), createObject('value', parameters('graphs')[copyIndex()].partitionKeyPaths), createObject('value', createArray()))]" - }, - "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.33.93.31351", - "templateHash": "3717641655060686556" - }, - "name": "DocumentDB Database Accounts Gremlin Databases Graphs", - "description": "This module deploys a DocumentDB Database Accounts Gremlin Database Graph." - }, - "parameters": { - "name": { - "type": "string", - "metadata": { - "description": "Required. Name of the graph." - } - }, - "tags": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Tags of the Gremlin graph resource." - } - }, - "databaseAccountName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent Database Account. Required if the template is used in a standalone deployment." - } - }, - "gremlinDatabaseName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent Gremlin Database. Required if the template is used in a standalone deployment." - } - }, - "indexingPolicy": { - "type": "object", - "defaultValue": {}, - "metadata": { - "description": "Optional. Indexing policy of the graph." - } - }, - "partitionKeyPaths": { - "type": "array", - "defaultValue": [], - "metadata": { - "description": "Optional. List of paths using which data within the container can be partitioned." - } - } - }, - "resources": { - "databaseAccount::gremlinDatabase": { - "existing": true, - "type": "Microsoft.DocumentDB/databaseAccounts/gremlinDatabases", - "apiVersion": "2023-04-15", - "name": "[format('{0}/{1}', parameters('databaseAccountName'), parameters('gremlinDatabaseName'))]" - }, - "databaseAccount": { - "existing": true, - "type": "Microsoft.DocumentDB/databaseAccounts", - "apiVersion": "2023-04-15", - "name": "[parameters('databaseAccountName')]" - }, - "gremlinGraph": { - "type": "Microsoft.DocumentDB/databaseAccounts/gremlinDatabases/graphs", - "apiVersion": "2023-04-15", - "name": "[format('{0}/{1}/{2}', parameters('databaseAccountName'), parameters('gremlinDatabaseName'), parameters('name'))]", - "tags": "[parameters('tags')]", - "properties": { - "resource": { - "id": "[parameters('name')]", - "indexingPolicy": "[if(not(empty(parameters('indexingPolicy'))), parameters('indexingPolicy'), null())]", - "partitionKey": { - "paths": "[if(not(empty(parameters('partitionKeyPaths'))), parameters('partitionKeyPaths'), null())]" - } - } - } - } - }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the graph." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the graph." - }, - "value": "[resourceId('Microsoft.DocumentDB/databaseAccounts/gremlinDatabases/graphs', parameters('databaseAccountName'), parameters('gremlinDatabaseName'), parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The name of the resource group the graph was created in." - }, - "value": "[resourceGroup().name]" - } - } - } - }, - "dependsOn": [ - "gremlinDatabase" - ] - } - }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the Gremlin database." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the Gremlin database." - }, - "value": "[resourceId('Microsoft.DocumentDB/databaseAccounts/gremlinDatabases', parameters('databaseAccountName'), parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The name of the resource group the Gremlin database was created in." - }, - "value": "[resourceGroup().name]" - } + "description": "Optional. The end IP address of the firewall rule. Must be IPv4 format. Must be greater than or equal to startIpAddress. Use value '0.0.0.0' for all Azure-internal IP addresses." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "keyType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the key. Must follow the [__] pattern." + } + }, + "serverKeyType": { + "type": "string", + "allowedValues": [ + "AzureKeyVault", + "ServiceManaged" + ], + "nullable": true, + "metadata": { + "description": "Optional. The server key type." + } + }, + "uri": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The URI of the server key. If the ServerKeyType is AzureKeyVault, then the URI is required. The AKV URI is required to be in this format: 'https://YourVaultName.azure.net/keys/YourKeyName/YourKeyVersion'." } } }, - "dependsOn": [ - "databaseAccount" - ] + "metadata": { + "__bicep_export!": true + } }, - "databaseAccount_tables": { - "copy": { - "name": "databaseAccount_tables", - "count": "[length(parameters('tables'))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-table-{1}', uniqueString(deployment().name, parameters('location')), parameters('tables')[copyIndex()].name)]", + "virtualNetworkRuleType": { + "type": "object", "properties": { - "expressionEvaluationOptions": { - "scope": "inner" + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the Server Virtual Network Rule." + } }, - "mode": "Incremental", - "parameters": { - "databaseAccountName": { - "value": "[parameters('name')]" - }, - "name": { - "value": "[parameters('tables')[copyIndex()].name]" - }, - "tags": { - "value": "[coalesce(tryGet(parameters('tables')[copyIndex()], 'tags'), parameters('tags'))]" - }, - "maxThroughput": { - "value": "[tryGet(parameters('tables')[copyIndex()], 'maxThroughput')]" - }, - "throughput": { - "value": "[tryGet(parameters('tables')[copyIndex()], 'throughput')]" + "virtualNetworkSubnetId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the virtual network subnet." } }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "languageVersion": "2.0", - "contentVersion": "1.0.0.0", + "ignoreMissingVnetServiceEndpoint": { + "type": "bool", + "nullable": true, "metadata": { - "_generator": { - "name": "bicep", - "version": "0.33.93.31351", - "templateHash": "8861672171630913677" - }, - "name": "Azure Cosmos DB account tables", - "description": "This module deploys a table within an Azure Cosmos DB Account." - }, - "parameters": { - "name": { - "type": "string", - "metadata": { - "description": "Required. Name of the table." - } - }, - "tags": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Tags for the table." - } - }, - "databaseAccountName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent Azure Cosmos DB account. Required if the template is used in a standalone deployment." - } - }, - "maxThroughput": { - "type": "int", - "defaultValue": 4000, - "metadata": { - "description": "Optional. Represents maximum throughput, the resource can scale up to. Cannot be set together with `throughput`. If `throughput` is set to something else than -1, this autoscale setting is ignored." - } - }, - "throughput": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. Request Units per second (for example 10000). Cannot be set together with `maxThroughput`." - } - } - }, - "resources": { - "databaseAccount": { - "existing": true, - "type": "Microsoft.DocumentDB/databaseAccounts", - "apiVersion": "2023-04-15", - "name": "[parameters('databaseAccountName')]" - }, - "table": { - "type": "Microsoft.DocumentDB/databaseAccounts/tables", - "apiVersion": "2023-04-15", - "name": "[format('{0}/{1}', parameters('databaseAccountName'), parameters('name'))]", - "tags": "[parameters('tags')]", - "properties": { - "options": "[if(contains(reference('databaseAccount').capabilities, createObject('name', 'EnableServerless')), createObject(), createObject('autoscaleSettings', if(equals(parameters('throughput'), null()), createObject('maxThroughput', parameters('maxThroughput')), null()), 'throughput', parameters('throughput')))]", - "resource": { - "id": "[parameters('name')]" - } - }, - "dependsOn": [ - "databaseAccount" - ] - } - }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the table." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the table." - }, - "value": "[resourceId('Microsoft.DocumentDB/databaseAccounts/tables', parameters('databaseAccountName'), parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The name of the resource group the table was created in." - }, - "value": "[resourceGroup().name]" - } + "description": "Optional. Allow creating a firewall rule before the virtual network has vnet service endpoint enabled." } } }, - "dependsOn": [ - "databaseAccount" - ] + "metadata": { + "__bicep_export!": true + } }, - "databaseAccount_privateEndpoints": { - "copy": { - "name": "databaseAccount_privateEndpoints", - "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-databaseAccount-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "resourceGroup": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupName'), '')]", + "securityAlerPolicyType": { + "type": "object", "properties": { - "expressionEvaluationOptions": { - "scope": "inner" + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the Security Alert Policy." + } }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'name'), format('pep-{0}-{1}-{2}', last(split(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '/')), coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service, copyIndex()))]" - }, - "privateLinkServiceConnections": "[if(not(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true())), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '/')), coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service, copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), 'groupIds', createArray(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service))))), createObject('value', null()))]", - "manualPrivateLinkServiceConnections": "[if(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true()), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '/')), coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service, copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), 'groupIds', createArray(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service), 'requestMessage', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualConnectionRequestMessage'), 'Manual approval required.'))))), createObject('value', null()))]", - "subnetResourceId": { - "value": "[coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId]" - }, - "enableTelemetry": { - "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'enableTelemetry'), parameters('enableTelemetry'))]" - }, - "location": { - "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'location'), reference(split(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location)]" - }, - "lock": { - "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" - }, - "privateDnsZoneGroup": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroup')]" - }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'roleAssignments')]" - }, - "tags": { - "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" - }, - "customDnsConfigs": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customDnsConfigs')]" - }, - "ipConfigurations": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'ipConfigurations')]" - }, - "applicationSecurityGroupResourceIds": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'applicationSecurityGroupResourceIds')]" - }, - "customNetworkInterfaceName": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customNetworkInterfaceName')]" + "disabledAlerts": { + "type": "array", + "allowedValues": [ + "Access_Anomaly", + "Brute_Force", + "Data_Exfiltration", + "Sql_Injection", + "Sql_Injection_Vulnerability", + "Unsafe_Action" + ], + "nullable": true, + "metadata": { + "description": "Optional. Alerts to disable." } }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "languageVersion": "2.0", - "contentVersion": "1.0.0.0", + "emailAccountAdmins": { + "type": "bool", + "nullable": true, "metadata": { - "_generator": { - "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "1277254088602407590" - }, - "name": "Private Endpoints", - "description": "This module deploys a Private Endpoint.", - "owner": "Azure/module-maintainers" - }, - "definitions": { - "privateDnsZoneGroupType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the Private DNS Zone Group." - } - }, - "privateDnsZoneGroupConfigs": { - "type": "array", - "items": { - "$ref": "#/definitions/privateDnsZoneGroupConfigType" - }, - "metadata": { - "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." - } - } - } - }, - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } - } - } - }, - "nullable": true - }, - "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." - } - } - }, - "nullable": true - }, - "ipConfigurationsType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the resource that is unique within a resource group." - } - }, - "properties": { - "type": "object", - "properties": { - "groupId": { - "type": "string", - "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." - } - }, - "memberName": { - "type": "string", - "metadata": { - "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." - } - }, - "privateIPAddress": { - "type": "string", - "metadata": { - "description": "Required. A private IP address obtained from the private endpoint's subnet." - } - } - }, - "metadata": { - "description": "Required. Properties of private endpoint IP configurations." - } - } - } - }, - "nullable": true - }, - "manualPrivateLinkServiceConnectionsType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the private link service connection." - } - }, - "properties": { - "type": "object", - "properties": { - "groupIds": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." - } - }, - "privateLinkServiceId": { - "type": "string", - "metadata": { - "description": "Required. The resource id of private link service." - } - }, - "requestMessage": { - "type": "string", - "metadata": { - "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." - } - } - }, - "metadata": { - "description": "Required. Properties of private link service connection." - } - } - } - }, - "nullable": true - }, - "privateLinkServiceConnectionsType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the private link service connection." - } - }, - "properties": { - "type": "object", - "properties": { - "groupIds": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." - } - }, - "privateLinkServiceId": { - "type": "string", - "metadata": { - "description": "Required. The resource id of private link service." - } - }, - "requestMessage": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." - } - } - }, - "metadata": { - "description": "Required. Properties of private link service connection." - } - } - } - }, - "nullable": true - }, - "customDnsConfigType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "fqdn": { - "type": "string", - "metadata": { - "description": "Required. Fqdn that resolves to private endpoint IP address." - } - }, - "ipAddresses": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. A list of private IP addresses of the private endpoint." - } - } - } - }, - "nullable": true - }, - "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_imported_from!": { - "sourceTemplate": "private-dns-zone-group/main.bicep" - } - } - } + "description": "Optional. Specifies that the alert is sent to the account administrators." + } + }, + "emailAddresses": { + "type": "array", + "items": { + "type": "string" }, - "parameters": { - "name": { - "type": "string", - "metadata": { - "description": "Required. Name of the private endpoint resource to create." - } - }, - "subnetResourceId": { - "type": "string", - "metadata": { - "description": "Required. Resource ID of the subnet where the endpoint needs to be created." - } - }, - "applicationSecurityGroupResourceIds": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. Application security groups in which the private endpoint IP configuration is included." - } - }, - "customNetworkInterfaceName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The custom name of the network interface attached to the private endpoint." - } - }, - "ipConfigurations": { - "$ref": "#/definitions/ipConfigurationsType", - "metadata": { - "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." - } - }, - "privateDnsZoneGroup": { - "$ref": "#/definitions/privateDnsZoneGroupType", - "nullable": true, - "metadata": { - "description": "Optional. The private DNS zone group to configure for the private endpoint." - } - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]", - "metadata": { - "description": "Optional. Location for all Resources." - } - }, - "lock": { - "$ref": "#/definitions/lockType", - "metadata": { - "description": "Optional. The lock settings of the service." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "tags": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." - } - }, - "customDnsConfigs": { - "$ref": "#/definitions/customDnsConfigType", - "metadata": { - "description": "Optional. Custom DNS configurations." - } - }, - "manualPrivateLinkServiceConnections": { - "$ref": "#/definitions/manualPrivateLinkServiceConnectionsType", - "metadata": { - "description": "Optional. 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." - } - }, - "privateLinkServiceConnections": { - "$ref": "#/definitions/privateLinkServiceConnectionsType", - "metadata": { - "description": "Optional. A grouping of information about the connection to the remote resource." - } - }, - "enableTelemetry": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Optional. Enable/Disable usage telemetry for module." - } - } + "nullable": true, + "metadata": { + "description": "Optional. Specifies an array of email addresses to which the alert is sent." + } + }, + "retentionDays": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Specifies the number of days to keep in the Threat Detection audit logs." + } + }, + "state": { + "type": "string", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specifies the state of the policy, whether it is enabled or disabled or a policy has not been applied yet on the specific database." + } + }, + "storageAccountAccessKey": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specifies the identifier key of the Threat Detection audit storage account." + } + }, + "storageEndpoint": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specifies the blob storage endpoint. This blob storage will hold all Threat Detection audit logs." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "failoverGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the failover group." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "databases": { + "type": "array", + "items": { + "type": "string" }, - "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 (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" - } + "metadata": { + "description": "Required. List of databases in the failover group." + } + }, + "partnerServers": { + "type": "array", + "items": { + "type": "string" }, - "resources": { - "avmTelemetry": { - "condition": "[parameters('enableTelemetry')]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2024-03-01", - "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.7.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": "2023-11-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": "[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": "2022-09-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.29.47.4906", - "templateHash": "5805178546717255803" - }, - "name": "Private Endpoint Private DNS Zone Groups", - "description": "This module deploys a Private Endpoint Private DNS Zone Group.", - "owner": "Azure/module-maintainers" - }, - "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 - } - } - }, - "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." - } - } - }, - "variables": { - "copy": [ - { - "name": "privateDnsZoneConfigsVar", - "count": "[length(parameters('privateDnsZoneConfigs'))]", - "input": { - "name": "[coalesce(tryGet(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')], 'name'), last(split(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId, '/')))]", - "properties": { - "privateDnsZoneId": "[parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId]" - } - } - } - ] - }, - "resources": { - "privateEndpoint": { - "existing": true, - "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2023-11-01", - "name": "[parameters('privateEndpointName')]" - }, - "privateDnsZoneGroup": { - "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2023-11-01", - "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", - "properties": { - "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" - }, - "dependsOn": [ - "privateEndpoint" - ] - } - }, - "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" - ] - } + "metadata": { + "description": "Required. List of the partner servers for the failover group." + } + }, + "readOnlyEndpoint": { + "$ref": "#/definitions/failoverGroupReadOnlyEndpointType", + "nullable": true, + "metadata": { + "description": "Optional. Read-only endpoint of the failover group instance." + } + }, + "readWriteEndpoint": { + "$ref": "#/definitions/failoverGroupReadWriteEndpointType", + "metadata": { + "description": "Required. Read-write endpoint of the failover group instance." + } + }, + "secondaryType": { + "type": "string", + "allowedValues": [ + "Geo", + "Standby" + ], + "metadata": { + "description": "Required. Databases secondary type on partner server." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "_1.privateEndpointCustomDnsConfigType": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. FQDN that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" }, - "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": { + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "_1.privateEndpointIpConfigurationType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource that is unique within a resource group." + } + }, + "properties": { + "type": "object", + "properties": { + "groupId": { "type": "string", "metadata": { - "description": "The name of the private endpoint." - }, - "value": "[parameters('name')]" + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + } }, - "location": { + "memberName": { "type": "string", "metadata": { - "description": "The location the resource was deployed into." - }, - "value": "[reference('privateEndpoint', '2023-11-01', 'full').location]" - }, - "customDnsConfig": { - "$ref": "#/definitions/customDnsConfigType", - "metadata": { - "description": "The custom DNS configurations of the private endpoint." - }, - "value": "[reference('privateEndpoint').customDnsConfigs]" - }, - "networkInterfaceIds": { - "type": "array", - "metadata": { - "description": "The IDs of the network interfaces associated with the private endpoint." - }, - "value": "[reference('privateEndpoint').networkInterfaces]" + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to." + } }, - "groupId": { + "privateIPAddress": { "type": "string", "metadata": { - "description": "The group Id for the private endpoint Group." - }, - "value": "[if(and(not(empty(reference('privateEndpoint').manualPrivateLinkServiceConnections)), greater(length(tryGet(reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties, 'groupIds')), 0)), coalesce(tryGet(reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties, 'groupIds', 0), ''), if(and(not(empty(reference('privateEndpoint').privateLinkServiceConnections)), greater(length(tryGet(reference('privateEndpoint').privateLinkServiceConnections[0].properties, 'groupIds')), 0)), coalesce(tryGet(reference('privateEndpoint').privateLinkServiceConnections[0].properties, 'groupIds', 0), ''), ''))]" + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } } + }, + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." } } }, - "dependsOn": [ - "databaseAccount" - ] + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } }, - "secretsExport": { - "condition": "[not(equals(parameters('secretsExportConfiguration'), null()))]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-secrets-kv', uniqueString(deployment().name, parameters('location')))]", - "subscriptionId": "[split(coalesce(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '//'), '/')[2]]", - "resourceGroup": "[split(coalesce(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '////'), '/')[4]]", + "_1.privateEndpointPrivateDnsZoneGroupType": { + "type": "object", "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "keyVaultName": { - "value": "[last(split(coalesce(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '//'), '/'))]" - }, - "secretsToSet": { - "value": "[union(createArray(), if(contains(parameters('secretsExportConfiguration'), 'primaryWriteKeySecretName'), createArray(createObject('name', parameters('secretsExportConfiguration').primaryWriteKeySecretName, 'value', listKeys(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '2023-04-15').primaryMasterKey)), createArray()), if(contains(parameters('secretsExportConfiguration'), 'primaryReadOnlyKeySecretName'), createArray(createObject('name', parameters('secretsExportConfiguration').primaryReadOnlyKeySecretName, 'value', listKeys(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '2023-04-15').primaryReadonlyMasterKey)), createArray()), if(contains(parameters('secretsExportConfiguration'), 'primaryWriteConnectionStringSecretName'), createArray(createObject('name', parameters('secretsExportConfiguration').primaryWriteConnectionStringSecretName, 'value', listConnectionStrings(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '2023-04-15').connectionStrings[0].connectionString)), createArray()), if(contains(parameters('secretsExportConfiguration'), 'primaryReadonlyConnectionStringSecretName'), createArray(createObject('name', parameters('secretsExportConfiguration').primaryReadonlyConnectionStringSecretName, 'value', listConnectionStrings(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '2023-04-15').connectionStrings[2].connectionString)), createArray()), if(contains(parameters('secretsExportConfiguration'), 'secondaryWriteKeySecretName'), createArray(createObject('name', parameters('secretsExportConfiguration').secondaryWriteKeySecretName, 'value', listKeys(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '2023-04-15').secondaryMasterKey)), createArray()), if(contains(parameters('secretsExportConfiguration'), 'secondaryReadonlyKeySecretName'), createArray(createObject('name', parameters('secretsExportConfiguration').secondaryReadonlyKeySecretName, 'value', listKeys(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '2023-04-15').secondaryReadonlyMasterKey)), createArray()), if(contains(parameters('secretsExportConfiguration'), 'secondaryWriteConnectionStringSecretName'), createArray(createObject('name', parameters('secretsExportConfiguration').secondaryWriteConnectionStringSecretName, 'value', listConnectionStrings(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '2023-04-15').connectionStrings[1].connectionString)), createArray()), if(contains(parameters('secretsExportConfiguration'), 'secondaryReadonlyConnectionStringSecretName'), createArray(createObject('name', parameters('secretsExportConfiguration').secondaryReadonlyConnectionStringSecretName, 'value', listConnectionStrings(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '2023-04-15').connectionStrings[3].connectionString)), createArray()))]" + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." } }, - "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.33.93.31351", - "templateHash": "8130113748408624333" - } - }, - "definitions": { - "secretSetType": { - "type": "object", - "properties": { - "secretResourceId": { - "type": "string", - "metadata": { - "description": "The resourceId of the exported secret." - } - }, - "secretUri": { - "type": "string", - "metadata": { - "description": "The secret URI of the exported secret." - } + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS Zone Group config." } }, - "metadata": { - "__bicep_export!": true - } - }, - "secretToSetType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the secret to set." - } - }, - "value": { - "type": "securestring", - "metadata": { - "description": "Required. The value of the secret to set." - } + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." } } } }, - "parameters": { - "keyVaultName": { - "type": "string", - "metadata": { - "description": "Required. The name of the Key Vault to set the ecrets in." - } - }, - "secretsToSet": { - "type": "array", - "items": { - "$ref": "#/definitions/secretToSetType" + "metadata": { + "description": "Required. The private DNS Zone Groups to associate the Private Endpoint. A DNS Zone Group can support up to 5 DNS zones." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "_1.secretSetOutputType": { + "type": "object", + "properties": { + "secretResourceId": { + "type": "string", + "metadata": { + "description": "The resourceId of the exported secret." + } + }, + "secretUri": { + "type": "string", + "metadata": { + "description": "The secret URI of the exported secret." + } + }, + "secretUriWithVersion": { + "type": "string", + "metadata": { + "description": "The secret URI with version of the exported secret." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for the output of the secret set via the secrets export feature.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "customerManagedKeyWithAutoRotateType": { + "type": "object", + "properties": { + "keyVaultResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of a key vault to reference a customer managed key for encryption from." + } + }, + "keyName": { + "type": "string", + "metadata": { + "description": "Required. The name of the customer managed key to use for encryption." + } + }, + "keyVersion": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The version of the customer managed key to reference for encryption. If not provided, using version as per 'autoRotationEnabled' setting." + } + }, + "autoRotationEnabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable auto-rotating to the latest key version. Default is `true`. If set to `false`, the latest key version at the time of the deployment is used." + } + }, + "userAssignedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a customer-managed key. To be used if the resource type supports auto-rotation of the customer-managed key.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "databaseSkuType": { + "type": "object", + "properties": { + "capacity": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The capacity of the particular SKU." + } + }, + "family": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. If the service has different generations of hardware, for the same SKU, then that can be captured here." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the SKU, typically, a letter + Number code, e.g. P3." + } + }, + "size": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Size of the particular SKU." + } + }, + "tier": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The tier or edition of the particular SKU, e.g. Basic, Premium." + } + } + }, + "metadata": { + "description": "The database SKU.", + "__bicep_imported_from!": { + "sourceTemplate": "database/main.bicep" + } + } + }, + "diagnosticSettingFullType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } }, - "metadata": { - "description": "Required. The secrets to set in the Key Vault." - } - } - }, - "resources": { - "keyVault": { - "existing": true, - "type": "Microsoft.KeyVault/vaults", - "apiVersion": "2022-07-01", - "name": "[parameters('keyVaultName')]" - }, - "secrets": { - "copy": { - "name": "secrets", - "count": "[length(parameters('secretsToSet'))]" + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } }, - "type": "Microsoft.KeyVault/vaults/secrets", - "apiVersion": "2023-07-01", - "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('secretsToSet')[copyIndex()].name)]", - "properties": { - "value": "[parameters('secretsToSet')[copyIndex()].value]" + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } } } }, - "outputs": { - "secretsSet": { - "type": "array", - "items": { - "$ref": "#/definitions/secretSetType" - }, - "metadata": { - "description": "The references to the secrets exported to the provided Key Vault." + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "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." + } }, - "copy": { - "count": "[length(range(0, length(coalesce(parameters('secretsToSet'), createArray()))))]", - "input": { - "secretResourceId": "[resourceId('Microsoft.KeyVault/vaults/secrets', parameters('keyVaultName'), parameters('secretsToSet')[range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()]].name)]", - "secretUri": "[reference(format('secrets[{0}]', range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()])).secretUri]" + "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." } } }, - "dependsOn": [ - "databaseAccount" - ] - } - }, - "outputs": { - "exportedSecrets": { - "$ref": "#/definitions/secretsOutputType", "metadata": { - "description": "The references to the secrets exported to the provided Key Vault." + "description": "An AVM-aligned type for a diagnostic setting. To be used if both logs & metrics are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "elasticPoolPerDatabaseSettingsType": { + "type": "object", + "properties": { + "autoPauseDelay": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Auto Pause Delay for per database within pool." + } + }, + "maxCapacity": { + "type": "string", + "metadata": { + "description": "Required. The maximum capacity any one database can consume. Examples: '0.5', '2'." + } + }, + "minCapacity": { + "type": "string", + "metadata": { + "description": "Required. The minimum capacity all databases are guaranteed. Examples: '0.5', '1'." + } + } + }, + "metadata": { + "description": "The per database settings for the elastic pool.", + "__bicep_imported_from!": { + "sourceTemplate": "elastic-pool/main.bicep" + } + } + }, + "elasticPoolSkuType": { + "type": "object", + "properties": { + "capacity": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The capacity of the particular SKU." + } + }, + "family": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. If the service has different generations of hardware, for the same SKU, then that can be captured here." + } + }, + "name": { + "type": "string", + "allowedValues": [ + "BC_DC", + "BC_Gen5", + "BasicPool", + "GP_DC", + "GP_FSv2", + "GP_Gen5", + "HS_Gen5", + "HS_MOPRMS", + "HS_PRMS", + "PremiumPool", + "ServerlessPool", + "StandardPool" + ], + "metadata": { + "description": "Required. The name of the SKU, typically, a letter + Number code, e.g. P3." + } + }, + "size": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Size of the particular SKU." + } + }, + "tier": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The tier or edition of the particular SKU, e.g. Basic, Premium." + } + } + }, + "metadata": { + "description": "The elastic pool SKU.", + "__bicep_imported_from!": { + "sourceTemplate": "elastic-pool/main.bicep" + } + } + }, + "failoverGroupReadOnlyEndpointType": { + "type": "object", + "properties": { + "failoverPolicy": { + "type": "string", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "metadata": { + "description": "Required. Failover policy of the read-only endpoint for the failover group." + } + }, + "targetServer": { + "type": "string", + "metadata": { + "description": "Required. The target partner server where the read-only endpoint points to." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "failover-group/main.bicep" + } + } + }, + "failoverGroupReadWriteEndpointType": { + "type": "object", + "properties": { + "failoverPolicy": { + "type": "string", + "allowedValues": [ + "Automatic", + "Manual" + ], + "metadata": { + "description": "Required. Failover policy of the read-write endpoint for the failover group. If failoverPolicy is Automatic then failoverWithDataLossGracePeriodMinutes is required." + } + }, + "failoverWithDataLossGracePeriodMinutes": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Grace period before failover with data loss is attempted for the read-write endpoint." + } + } }, - "value": "[if(not(equals(parameters('secretsExportConfiguration'), null())), toObject(reference('secretsExport').outputs.secretsSet.value, lambda('secret', last(split(lambdaVariables('secret').secretResourceId, '/'))), lambda('secret', lambdaVariables('secret'))), createObject())]" - }, - "name": { - "type": "string", "metadata": { - "description": "The name of the database account." - }, - "value": "[parameters('name')]" + "__bicep_imported_from!": { + "sourceTemplate": "failover-group/main.bicep" + } + } }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the database account." + "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." + } + } }, - "value": "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", "metadata": { - "description": "The name of the resource group the database account was created in." - }, - "value": "[resourceGroup().name]" + "description": "An AVM-aligned type for a lock.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } }, - "systemAssignedMIPrincipalId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "The principal ID of the system assigned identity." + "longTermBackupRetentionPolicyType": { + "type": "object", + "properties": { + "backupStorageAccessTier": { + "type": "string", + "allowedValues": [ + "Archive", + "Hot" + ], + "nullable": true, + "metadata": { + "description": "Optional. The BackupStorageAccessTier for the LTR backups." + } + }, + "makeBackupsImmutable": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. The setting whether to make LTR backups immutable." + } + }, + "monthlyRetention": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Monthly retention in ISO 8601 duration format." + } + }, + "weeklyRetention": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Weekly retention in ISO 8601 duration format." + } + }, + "weekOfYear": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Week of year backup to keep for yearly retention." + } + }, + "yearlyRetention": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Yearly retention in ISO 8601 duration format." + } + } }, - "value": "[tryGet(tryGet(reference('databaseAccount', '2023-04-15', 'full'), 'identity'), 'principalId')]" - }, - "location": { - "type": "string", "metadata": { - "description": "The location the resource was deployed into." - }, - "value": "[reference('databaseAccount', '2023-04-15', 'full').location]" + "description": "The long-term backup retention policy for the database.", + "__bicep_imported_from!": { + "sourceTemplate": "database/main.bicep" + } + } }, - "endpoint": { - "type": "string", - "metadata": { - "description": "The endpoint of the database account." + "managedIdentityAllType": { + "type": "object", + "properties": { + "systemAssigned": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enables system assigned managed identity on the resource." + } + }, + "userAssignedResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The resource ID(s) to assign to the resource. Required if a user assigned identity is used for encryption." + } + } }, - "value": "[reference('databaseAccount').documentEndpoint]" - }, - "privateEndpoints": { - "type": "array", "metadata": { - "description": "The private endpoints of the database account." - }, - "copy": { - "count": "[length(if(not(empty(parameters('privateEndpoints'))), array(parameters('privateEndpoints')), createArray()))]", - "input": { - "name": "[reference(format('databaseAccount_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", - "resourceId": "[reference(format('databaseAccount_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", - "groupId": "[reference(format('databaseAccount_privateEndpoints[{0}]', copyIndex())).outputs.groupId.value]", - "customDnsConfig": "[reference(format('databaseAccount_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfig.value]", - "networkInterfaceIds": "[reference(format('databaseAccount_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceIds.value]" + "description": "An AVM-aligned type for a managed identity configuration. To be used if both a system-assigned & user-assigned identities are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } - } - } - } - }, - "dependsOn": [ - "privateDnsZone" - ] - } - }, - "outputs": { - "resourceId": { - "type": "string", - "value": "[reference('cosmosDb').outputs.resourceId.value]" - }, - "cosmosDBname": { - "type": "string", - "value": "[reference('cosmosDb').outputs.name.value]" - } - } - } - }, - "dependsOn": [ - "logAnalyticsWorkspace", - "network" - ] - }, - "sqlServer": { - "condition": "[parameters('sqlServerEnabled')]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[take(format('{0}-sqlserver-deployment', parameters('name')), 64)]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[format('sql{0}{1}', parameters('name'), variables('resourceToken'))]" - }, - "administratorLogin": { - "value": "[variables('servicesUsername')]" - }, - "administratorLoginPassword": { - "value": "[parameters('vmAdminPasswordOrKey')]" - }, - "databases": { - "value": "[parameters('sqlServerDatabases')]" - }, - "location": { - "value": "[parameters('location')]" - }, - "networkIsolation": { - "value": "[parameters('networkIsolation')]" - }, - "virtualNetworkResourceId": "[if(parameters('networkIsolation'), createObject('value', reference('network').outputs.virtualNetworkId.value), createObject('value', ''))]", - "virtualNetworkSubnetResourceId": "[if(parameters('networkIsolation'), createObject('value', reference('network').outputs.vmSubnetId.value), createObject('value', ''))]", - "tags": { - "value": "[variables('allTags')]" - } - }, - "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.1.42791", - "templateHash": "18362011243916437863" - } - }, - "definitions": { - "_1.diagnosticSettingFullType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the diagnostic setting." - } - }, - "logCategoriesAndGroups": { - "type": "array", - "items": { - "type": "object", - "properties": { - "category": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + }, + "privateEndpointSingleServiceType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private Endpoint." + } + }, + "location": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The location to deploy the Private Endpoint to." + } + }, + "privateLinkServiceConnectionName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private link connection to create." + } + }, + "service": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The subresource to deploy the Private Endpoint for. For example \"vault\" for a Key Vault Private Endpoint." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + } + }, + "resourceGroupResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the Resource Group the Private Endpoint will be created in. If not specified, the Resource Group of the provided Virtual Network Subnet is used." + } + }, + "privateDnsZoneGroup": { + "$ref": "#/definitions/_1.privateEndpointPrivateDnsZoneGroupType", + "nullable": true, + "metadata": { + "description": "Optional. The private DNS Zone Group to configure for the Private Endpoint." + } + }, + "isManualConnection": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. If Manual Private Link Connection is required." + } + }, + "manualConnectionRequestMessage": { + "type": "string", + "nullable": true, + "maxLength": 140, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with the manual connection request." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/_1.privateEndpointCustomDnsConfigType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Custom DNS configurations." + } + }, + "ipConfigurations": { + "type": "array", + "items": { + "$ref": "#/definitions/_1.privateEndpointIpConfigurationType" + }, + "nullable": true, + "metadata": { + "description": "Optional. A list of IP configurations of the Private Endpoint. This will be used to map to the first-party Service endpoints." + } + }, + "applicationSecurityGroupResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Application security groups in which the Private Endpoint IP configuration is included." + } + }, + "customNetworkInterfaceName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The custom name of the network interface attached to the Private Endpoint." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags to be applied on all resources/Resource Groups in this deployment." + } + }, + "enableTelemetry": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } } }, - "categoryGroup": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + "metadata": { + "description": "An AVM-aligned type for a private endpoint. To be used if the private endpoint's default service / groupId can be assumed (i.e., for services that only have one Private Endpoint type like 'vault' for key vault).", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "recurringScansType": { + "type": "object", + "properties": { + "emails": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. Specifies an array of e-mail addresses to which the scan notification is sent." + } + }, + "emailSubscriptionAdmins": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Specifies that the schedule scan notification will be sent to the subscription administrators." + } + }, + "isEnabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Recurring scans state." + } } }, - "enabled": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Enable or disable the category explicitly. Default is `true`." + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "vulnerability-assessment/main.bicep" } } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." - } - }, - "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." + }, + "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." + } } }, - "enabled": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Enable or disable the category explicitly. Default is `true`." + "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.5.1" } } - } - }, - "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 both logs & metrics are supported by the resource provider.", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } - } - }, - "_2.databaseSkuType": { - "type": "object", - "properties": { - "capacity": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The capacity of the particular SKU." - } - }, - "family": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. If the service has different generations of hardware, for the same SKU, then that can be captured here." - } - }, - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the SKU, typically, a letter + Number code, e.g. P3." - } - }, - "size": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Size of the particular SKU." - } - }, - "tier": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The tier or edition of the particular SKU, e.g. Basic, Premium." - } - } - }, - "metadata": { - "description": "The database SKU.", - "__bicep_imported_from!": { - "sourceTemplate": "customTypes.bicep" - } - } - }, - "_2.longTermBackupRetentionPolicyType": { - "type": "object", - "properties": { - "backupStorageAccessTier": { - "type": "string", - "allowedValues": [ - "Archive", - "Hot" - ], - "nullable": true, - "metadata": { - "description": "Optional. The BackupStorageAccessTier for the LTR backups." - } - }, - "makeBackupsImmutable": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. The setting whether to make LTR backups immutable." - } - }, - "monthlyRetention": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Monthly retention in ISO 8601 duration format." - } - }, - "weeklyRetention": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Weekly retention in ISO 8601 duration format." - } - }, - "weekOfYear": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. Week of year backup to keep for yearly retention." - } - }, - "yearlyRetention": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Yearly retention in ISO 8601 duration format." - } - } - }, - "metadata": { - "description": "The long-term backup retention policy for the database.", - "__bicep_imported_from!": { - "sourceTemplate": "customTypes.bicep" - } - } - }, - "_2.shortTermBackupRetentionPolicyType": { - "type": "object", - "properties": { - "diffBackupIntervalInHours": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. Differential backup interval in hours. For Hyperscale tiers this value will be ignored." - } - }, - "retentionDays": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. Point-in-time retention in days." - } - } - }, - "metadata": { - "description": "The short-term backup retention policy for the database.", - "__bicep_imported_from!": { - "sourceTemplate": "customTypes.bicep" - } - } - }, - "databasePropertyType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the Elastic Pool." - } - }, - "tags": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Tags of the resource." - } - }, - "sku": { - "$ref": "#/definitions/_2.databaseSkuType", - "nullable": true, - "metadata": { - "description": "Optional. The database SKU." - } - }, - "autoPauseDelay": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. Time in minutes after which database is automatically paused. A value of -1 means that automatic pause is disabled." - } - }, - "availabilityZone": { - "type": "string", - "allowedValues": [ - "1", - "2", - "3", - "NoPreference" - ], - "nullable": true, - "metadata": { - "description": "Optional. Specifies the availability zone the database is pinned to." - } - }, - "catalogCollation": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Collation of the metadata catalog." - } - }, - "collation": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The collation of the database." - } - }, - "createMode": { - "type": "string", - "allowedValues": [ - "Copy", - "Default", - "OnlineSecondary", - "PointInTimeRestore", - "Recovery", - "Restore", - "RestoreExternalBackup", - "RestoreExternalBackupSecondary", - "RestoreLongTermRetentionBackup", - "Secondary" - ], - "nullable": true, - "metadata": { - "description": "Optional. Specifies the mode of database creation." - } - }, - "elasticPoolResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The resource identifier of the elastic pool containing this database." - } - }, - "encryptionProtector": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The azure key vault URI of the database if it's configured with per Database Customer Managed Keys." - } - }, - "encryptionProtectorAutoRotation": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. The flag to enable or disable auto rotation of database encryption protector AKV key." - } - }, - "federatedClientId": { - "type": "string", - "nullable": true, - "minLength": 36, - "maxLength": 36, - "metadata": { - "description": "Optional. The Client id used for cross tenant per database CMK scenario." - } - }, - "freeLimitExhaustionBehavior": { - "type": "string", - "allowedValues": [ - "AutoPause", - "BillOverUsage" - ], - "nullable": true, - "metadata": { - "description": "Optional. Specifies the behavior when monthly free limits are exhausted for the free database." - } - }, - "highAvailabilityReplicaCount": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The number of secondary replicas associated with the database that are used to provide high availability. Not applicable to a Hyperscale database within an elastic pool." - } - }, - "isLedgerOn": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Whether or not this database is a ledger database, which means all tables in the database are ledger tables." - } - }, - "licenseType": { - "type": "string", - "allowedValues": [ - "BasePrice", - "LicenseIncluded" - ], - "nullable": true, - "metadata": { - "description": "Optional. The license type to apply for this database." - } - }, - "longTermRetentionBackupResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The resource identifier of the long term retention backup associated with create operation of this database." - } - }, - "maintenanceConfigurationId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Maintenance configuration id assigned to the database. This configuration defines the period when the maintenance updates will occur." - } - }, - "manualCutover": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Whether or not customer controlled manual cutover needs to be done during Update Database operation to Hyperscale tier." - } - }, - "maxSizeBytes": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The max size of the database expressed in bytes." - } - }, - "minCapacity": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Minimal capacity that database will always have allocated, if not paused." - } - }, - "performCutover": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. To trigger customer controlled manual cutover during the wait state while Scaling operation is in progress." - } - }, - "preferredEnclaveType": { - "type": "string", - "allowedValues": [ - "Default", - "VBS" - ], - "nullable": true, - "metadata": { - "description": "Optional. Type of enclave requested on the database." - } - }, - "readScale": { - "type": "string", - "allowedValues": [ - "Disabled", - "Enabled" - ], - "nullable": true, - "metadata": { - "description": "Optional. The state of read-only routing. If enabled, connections that have application intent set to readonly in their connection string may be routed to a readonly secondary replica in the same region. Not applicable to a Hyperscale database within an elastic pool." - } - }, - "recoverableDatabaseResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The resource identifier of the recoverable database associated with create operation of this database." - } - }, - "recoveryServicesRecoveryPointResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The resource identifier of the recovery point associated with create operation of this database." - } - }, - "requestedBackupStorageRedundancy": { - "type": "string", - "allowedValues": [ - "Geo", - "GeoZone", - "Local", - "Zone" - ], - "nullable": true, - "metadata": { - "description": "Optional. The storage account type to be used to store backups for this database." - } - }, - "restorableDroppedDatabaseResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The resource identifier of the restorable dropped database associated with create operation of this database." - } - }, - "restorePointInTime": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Specifies the point in time (ISO8601 format) of the source database that will be restored to create the new database." - } - }, - "sampleName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the sample schema to apply when creating this database." - } - }, - "secondaryType": { - "type": "string", - "allowedValues": [ - "Geo", - "Named", - "Standby" - ], - "nullable": true, - "metadata": { - "description": "Optional. The secondary type of the database if it is a secondary." - } - }, - "sourceDatabaseDeletionDate": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Specifies the time that the database was deleted." - } - }, - "sourceDatabaseResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The resource identifier of the source database associated with create operation of this database." - } - }, - "sourceResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The resource identifier of the source associated with the create operation of this database." - } - }, - "useFreeLimit": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Whether or not the database uses free monthly limits. Allowed on one database in a subscription." - } - }, - "zoneRedundant": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Whether or not this database is zone redundant, which means the replicas of this database will be spread across multiple availability zones." - } - }, - "diagnosticSettings": { - "type": "array", - "items": { - "$ref": "#/definitions/_1.diagnosticSettingFullType" + }, + "secretsOutputType": { + "type": "object", + "properties": {}, + "additionalProperties": { + "$ref": "#/definitions/_1.secretSetOutputType", + "metadata": { + "description": "An exported secret's references." + } + }, + "metadata": { + "description": "A map of the exported secrets", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "shortTermBackupRetentionPolicyType": { + "type": "object", + "properties": { + "diffBackupIntervalInHours": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Differential backup interval in hours. For Hyperscale tiers this value will be ignored." + } + }, + "retentionDays": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Point-in-time retention in days." + } + } + }, + "metadata": { + "description": "The short-term backup retention policy for the database.", + "__bicep_imported_from!": { + "sourceTemplate": "database/main.bicep" + } + } + } }, - "nullable": true, - "metadata": { - "description": "Optional. The diagnostic settings of the service." - } - }, - "backupShortTermRetentionPolicy": { - "$ref": "#/definitions/_2.shortTermBackupRetentionPolicyType", - "nullable": true, - "metadata": { - "description": "Optional. The short term backup retention policy for the database." - } - }, - "backupLongTermRetentionPolicy": { - "$ref": "#/definitions/_2.longTermBackupRetentionPolicyType", - "nullable": true, - "metadata": { - "description": "Optional. The long term backup retention policy for the database." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "customTypes.bicep" - } - } - }, - "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.5.1" - } - } - } - }, - "parameters": { - "name": { - "type": "string", - "metadata": { - "description": "Name of the SQL Server instance." - } - }, - "location": { - "type": "string", - "metadata": { - "description": "Specifies the location for all the Azure resources." - } - }, - "tags": { - "type": "object", - "defaultValue": {}, - "metadata": { - "description": "Optional. Tags to be applied to the resources." - } - }, - "administratorLogin": { - "type": "string", - "metadata": { - "description": "Username for the SQL Server administrator." - } - }, - "administratorLoginPassword": { - "type": "securestring", - "metadata": { - "description": "Password for the SQL Server administrator." - } - }, - "virtualNetworkResourceId": { - "type": "string", - "metadata": { - "description": "Resource ID of the virtual network to link the private DNS zones." - } - }, - "virtualNetworkSubnetResourceId": { - "type": "string", - "metadata": { - "description": "Resource ID of the subnet for the private endpoint." - } - }, - "networkIsolation": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Specifies whether network isolation is enabled. This will create a private endpoint for the SQL Server instance and link the private DNS zone." - } - }, - "roleAssignments": { - "type": "array", - "items": { - "$ref": "#/definitions/roleAssignmentType" - }, - "nullable": true, - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "databases": { - "type": "array", - "items": { - "$ref": "#/definitions/databasePropertyType" - }, - "nullable": true, - "metadata": { - "description": "Optional. List of SQL Server databases to deploy." - } - } - }, - "variables": { - "nameFormatted": "[toLower(parameters('name'))]" - }, - "resources": { - "privateDnsZone": { - "condition": "[parameters('networkIsolation')]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "private-dns-sql-deployment", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[format('privatelink{0}', environment().suffixes.sqlServerHostname)]" + "parameters": { + "administratorLogin": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Conditional. The administrator username for the server. Required if no `administrators` object for AAD authentication is provided." + } + }, + "administratorLoginPassword": { + "type": "securestring", + "defaultValue": "", + "metadata": { + "description": "Conditional. The administrator login password. Required if no `administrators` object for AAD authentication is provided." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the server." + } + }, + "managedIdentities": { + "$ref": "#/definitions/managedIdentityAllType", + "nullable": true, + "metadata": { + "description": "Optional. The managed identity definition for this resource." + } + }, + "primaryUserAssignedIdentityId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Conditional. The resource ID of a user assigned identity to be used by default. Required if \"userAssignedIdentities\" is not empty." + } + }, + "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", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "databases": { + "type": "array", + "items": { + "$ref": "#/definitions/databasePropertyType" + }, + "defaultValue": [], + "metadata": { + "description": "Optional. The databases to create in the server." + } + }, + "elasticPools": { + "type": "array", + "items": { + "$ref": "#/definitions/elasticPoolPropertyType" + }, + "defaultValue": [], + "metadata": { + "description": "Optional. The Elastic Pools to create in the server." + } + }, + "firewallRules": { + "type": "array", + "items": { + "$ref": "#/definitions/firewallRuleType" + }, + "defaultValue": [], + "metadata": { + "description": "Optional. The firewall rules to create in the server." + } + }, + "virtualNetworkRules": { + "type": "array", + "items": { + "$ref": "#/definitions/virtualNetworkRuleType" + }, + "defaultValue": [], + "metadata": { + "description": "Optional. The virtual network rules to create in the server." + } + }, + "securityAlertPolicies": { + "type": "array", + "items": { + "$ref": "#/definitions/securityAlerPolicyType" + }, + "defaultValue": [], + "metadata": { + "description": "Optional. The security alert policies to create in the server." + } + }, + "keys": { + "type": "array", + "items": { + "$ref": "#/definitions/keyType" + }, + "defaultValue": [], + "metadata": { + "description": "Optional. The keys to configure." + } + }, + "customerManagedKey": { + "$ref": "#/definitions/customerManagedKeyWithAutoRotateType", + "nullable": true, + "metadata": { + "description": "Optional. The customer managed key definition for server TDE." + } + }, + "administrators": { + "$ref": "#/definitions/serverExternalAdministratorType", + "nullable": true, + "metadata": { + "description": "Conditional. The Azure Active Directory (AAD) administrator authentication. Required if no `administratorLogin` & `administratorLoginPassword` is provided." + } + }, + "federatedClientId": { + "type": "string", + "nullable": true, + "minLength": 36, + "maxLength": 36, + "metadata": { + "description": "Optional. The Client id used for cross tenant CMK scenario." + } + }, + "minimalTlsVersion": { + "type": "string", + "defaultValue": "1.2", + "allowedValues": [ + "1.0", + "1.1", + "1.2", + "1.3" + ], + "metadata": { + "description": "Optional. Minimal TLS version allowed." + } + }, + "isIPv6Enabled": { + "type": "string", + "defaultValue": "Disabled", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "metadata": { + "description": "Optional. Whether or not to enable IPv6 support for this server." + } + }, + "privateEndpoints": { + "type": "array", + "items": { + "$ref": "#/definitions/privateEndpointSingleServiceType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible." + } + }, + "publicNetworkAccess": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "", + "Enabled", + "Disabled", + "SecuredByPerimeter" + ], + "metadata": { + "description": "Optional. Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set and neither firewall rules nor virtual network rules are set." + } + }, + "restrictOutboundNetworkAccess": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "", + "Enabled", + "Disabled" + ], + "metadata": { + "description": "Optional. Whether or not to restrict outbound network access for this server." + } + }, + "vulnerabilityAssessmentsObj": { + "$ref": "#/definitions/vulnerabilityAssessmentType", + "nullable": true, + "metadata": { + "description": "Optional. The vulnerability assessment configuration." + } + }, + "auditSettings": { + "$ref": "#/definitions/auditSettingsType", + "defaultValue": { + "state": "Enabled" + }, + "metadata": { + "description": "Optional. The audit settings configuration. If you want to disable auditing, set the parmaeter to an empty object." + } + }, + "secretsExportConfiguration": { + "$ref": "#/definitions/secretsExportConfigurationType", + "nullable": true, + "metadata": { + "description": "Optional. Key vault reference and secret settings for the module's secrets export." + } + }, + "failoverGroups": { + "type": "array", + "items": { + "$ref": "#/definitions/failoverGroupType" + }, + "defaultValue": [], + "metadata": { + "description": "Optional. The failover groups configuration." + } + } }, - "virtualNetworkLinks": { - "value": [ + "variables": { + "copy": [ { - "virtualNetworkResourceId": "[parameters('virtualNetworkResourceId')]" + "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)))))]" + } + ], + "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", + "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned,UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', null())), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", + "enableReferencedModulesTelemetry": false, + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Reservation Purchaser": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f7b75c60-3036-4b75-91c3-6b41c27c1689')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "SQL DB Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9b7fa17d-e63e-47b0-bb0a-15c516ac86ec')]", + "SQL Managed Instance Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4939a1f6-9ae0-4e48-a1e0-f2cbe897382d')]", + "SQL Security Manager": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '056cd41c-7e88-42e1-933e-88ba6a50c9c3')]", + "SQL Server Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6d8ee4ec-f05a-4a1d-8b00-a9b17e38b437')]", + "SqlDb Migration Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '189207d4-bb67-4208-a635-b06afe8b2c57')]", + "SqlMI Migration Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1d335eef-eee1-47fe-a9e0-53214eba8872')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "cMKKeyVault::cMKKey": { + "condition": "[and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), not(empty(tryGet(parameters('customerManagedKey'), 'keyName')))))]", + "existing": true, + "type": "Microsoft.KeyVault/vaults/keys", + "apiVersion": "2023-07-01", + "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[2]]", + "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[4]]", + "name": "[format('{0}/{1}', last(split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')), tryGet(parameters('customerManagedKey'), 'keyName'))]" + }, + "cMKKeyVault": { + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId')))]", + "existing": true, + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2023-07-01", + "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[2]]", + "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[4]]", + "name": "[last(split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/'))]" + }, + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.sql-server.{0}.{1}', replace('0.15.0', '.', '-'), 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" + } + } + } } - ] - }, - "tags": { - "value": "[parameters('tags')]" - } - }, - "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.32.4.45862", - "templateHash": "83178825086050429" }, - "name": "Private DNS Zones", - "description": "This module deploys a Private DNS zone.", - "owner": "Azure/module-maintainers" - }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "type": "object", - "properties": { + "server": { + "type": "Microsoft.Sql/servers", + "apiVersion": "2023-08-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "identity": "[variables('identity')]", + "properties": { + "administratorLogin": "[if(not(empty(parameters('administratorLogin'))), parameters('administratorLogin'), null())]", + "administratorLoginPassword": "[if(not(empty(parameters('administratorLoginPassword'))), parameters('administratorLoginPassword'), null())]", + "administrators": "[union(createObject('administratorType', 'ActiveDirectory'), coalesce(parameters('administrators'), createObject()))]", + "federatedClientId": "[parameters('federatedClientId')]", + "isIPv6Enabled": "[parameters('isIPv6Enabled')]", + "keyId": "[if(not(equals(parameters('customerManagedKey'), null())), if(not(empty(tryGet(parameters('customerManagedKey'), 'keyVersion'))), format('{0}/{1}', reference('cMKKeyVault::cMKKey').keyUri, tryGet(parameters('customerManagedKey'), 'keyVersion')), if(coalesce(tryGet(parameters('customerManagedKey'), 'autoRotationEnabled'), true()), reference('cMKKeyVault::cMKKey').keyUri, reference('cMKKeyVault::cMKKey').keyUriWithVersion)), null())]", + "version": "12.0", + "minimalTlsVersion": "[parameters('minimalTlsVersion')]", + "primaryUserAssignedIdentityId": "[if(not(empty(parameters('primaryUserAssignedIdentityId'))), parameters('primaryUserAssignedIdentityId'), null())]", + "publicNetworkAccess": "[if(not(empty(parameters('publicNetworkAccess'))), parameters('publicNetworkAccess'), if(and(and(not(empty(parameters('privateEndpoints'))), empty(parameters('firewallRules'))), empty(parameters('virtualNetworkRules'))), 'Disabled', null()))]", + "restrictOutboundNetworkAccess": "[if(not(empty(parameters('restrictOutboundNetworkAccess'))), parameters('restrictOutboundNetworkAccess'), null())]" + }, + "dependsOn": [ + "cMKKeyVault::cMKKey" + ] + }, + "server_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.Sql/servers/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "server" + ] + }, + "server_roleAssignments": { + "copy": { + "name": "server_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Sql/servers/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Sql/servers', 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": [ + "server" + ] + }, + "server_databases": { + "copy": { + "name": "server_databases", + "count": "[length(parameters('databases'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Sql-DB-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "serverName": { + "value": "[parameters('name')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[coalesce(tryGet(parameters('databases')[copyIndex()], 'tags'), parameters('tags'))]" + }, "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + "value": "[parameters('databases')[copyIndex()].name]" + }, + "sku": { + "value": "[tryGet(parameters('databases')[copyIndex()], 'sku')]" + }, + "autoPauseDelay": { + "value": "[tryGet(parameters('databases')[copyIndex()], 'autoPauseDelay')]" + }, + "availabilityZone": { + "value": "[tryGet(parameters('databases')[copyIndex()], 'availabilityZone')]" + }, + "catalogCollation": { + "value": "[tryGet(parameters('databases')[copyIndex()], 'catalogCollation')]" + }, + "collation": { + "value": "[tryGet(parameters('databases')[copyIndex()], 'collation')]" + }, + "createMode": { + "value": "[tryGet(parameters('databases')[copyIndex()], 'createMode')]" + }, + "elasticPoolResourceId": { + "value": "[tryGet(parameters('databases')[copyIndex()], 'elasticPoolResourceId')]" + }, + "encryptionProtector": { + "value": "[tryGet(parameters('databases')[copyIndex()], 'encryptionProtector')]" + }, + "encryptionProtectorAutoRotation": { + "value": "[tryGet(parameters('databases')[copyIndex()], 'encryptionProtectorAutoRotation')]" + }, + "federatedClientId": { + "value": "[tryGet(parameters('databases')[copyIndex()], 'federatedClientId')]" + }, + "freeLimitExhaustionBehavior": { + "value": "[tryGet(parameters('databases')[copyIndex()], 'freeLimitExhaustionBehavior')]" + }, + "highAvailabilityReplicaCount": { + "value": "[tryGet(parameters('databases')[copyIndex()], 'highAvailabilityReplicaCount')]" + }, + "isLedgerOn": { + "value": "[tryGet(parameters('databases')[copyIndex()], 'isLedgerOn')]" + }, + "licenseType": { + "value": "[tryGet(parameters('databases')[copyIndex()], 'licenseType')]" + }, + "longTermRetentionBackupResourceId": { + "value": "[tryGet(parameters('databases')[copyIndex()], 'longTermRetentionBackupResourceId')]" + }, + "maintenanceConfigurationId": { + "value": "[tryGet(parameters('databases')[copyIndex()], 'maintenanceConfigurationId')]" + }, + "manualCutover": { + "value": "[tryGet(parameters('databases')[copyIndex()], 'manualCutover')]" + }, + "maxSizeBytes": { + "value": "[tryGet(parameters('databases')[copyIndex()], 'maxSizeBytes')]" + }, + "minCapacity": { + "value": "[tryGet(parameters('databases')[copyIndex()], 'minCapacity')]" + }, + "performCutover": { + "value": "[tryGet(parameters('databases')[copyIndex()], 'performCutover')]" + }, + "preferredEnclaveType": { + "value": "[tryGet(parameters('databases')[copyIndex()], 'preferredEnclaveType')]" + }, + "readScale": { + "value": "[tryGet(parameters('databases')[copyIndex()], 'readScale')]" + }, + "recoverableDatabaseResourceId": { + "value": "[tryGet(parameters('databases')[copyIndex()], 'recoverableDatabaseResourceId')]" + }, + "recoveryServicesRecoveryPointResourceId": { + "value": "[tryGet(parameters('databases')[copyIndex()], 'recoveryServicesRecoveryPointResourceId')]" + }, + "requestedBackupStorageRedundancy": { + "value": "[tryGet(parameters('databases')[copyIndex()], 'requestedBackupStorageRedundancy')]" + }, + "restorableDroppedDatabaseResourceId": { + "value": "[tryGet(parameters('databases')[copyIndex()], 'restorableDroppedDatabaseResourceId')]" + }, + "restorePointInTime": { + "value": "[tryGet(parameters('databases')[copyIndex()], 'restorePointInTime')]" + }, + "sampleName": { + "value": "[tryGet(parameters('databases')[copyIndex()], 'sampleName')]" + }, + "secondaryType": { + "value": "[tryGet(parameters('databases')[copyIndex()], 'secondaryType')]" + }, + "sourceDatabaseDeletionDate": { + "value": "[tryGet(parameters('databases')[copyIndex()], 'sourceDatabaseDeletionDate')]" + }, + "sourceDatabaseResourceId": { + "value": "[tryGet(parameters('databases')[copyIndex()], 'sourceDatabaseResourceId')]" + }, + "sourceResourceId": { + "value": "[tryGet(parameters('databases')[copyIndex()], 'sourceResourceId')]" + }, + "useFreeLimit": { + "value": "[tryGet(parameters('databases')[copyIndex()], 'useFreeLimit')]" + }, + "zoneRedundant": { + "value": "[tryGet(parameters('databases')[copyIndex()], 'zoneRedundant')]" + }, + "diagnosticSettings": { + "value": "[tryGet(parameters('databases')[copyIndex()], 'diagnosticSettings')]" + }, + "backupShortTermRetentionPolicy": { + "value": "[tryGet(parameters('databases')[copyIndex()], 'backupShortTermRetentionPolicy')]" + }, + "backupLongTermRetentionPolicy": { + "value": "[tryGet(parameters('databases')[copyIndex()], 'backupLongTermRetentionPolicy')]" + } + }, + "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.34.44.8038", + "templateHash": "10036542469147733417" + }, + "name": "SQL Server Database", + "description": "This module deploys an Azure SQL Server Database." + }, + "definitions": { + "databaseSkuType": { + "type": "object", + "properties": { + "capacity": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The capacity of the particular SKU." + } + }, + "family": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. If the service has different generations of hardware, for the same SKU, then that can be captured here." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the SKU, typically, a letter + Number code, e.g. P3." + } + }, + "size": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Size of the particular SKU." + } + }, + "tier": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The tier or edition of the particular SKU, e.g. Basic, Premium." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The database SKU." + } + }, + "shortTermBackupRetentionPolicyType": { + "type": "object", + "properties": { + "diffBackupIntervalInHours": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Differential backup interval in hours. For Hyperscale tiers this value will be ignored." + } + }, + "retentionDays": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Point-in-time retention in days." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The short-term backup retention policy for the database." + } + }, + "longTermBackupRetentionPolicyType": { + "type": "object", + "properties": { + "backupStorageAccessTier": { + "type": "string", + "allowedValues": [ + "Archive", + "Hot" + ], + "nullable": true, + "metadata": { + "description": "Optional. The BackupStorageAccessTier for the LTR backups." + } + }, + "makeBackupsImmutable": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. The setting whether to make LTR backups immutable." + } + }, + "monthlyRetention": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Monthly retention in ISO 8601 duration format." + } + }, + "weeklyRetention": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Weekly retention in ISO 8601 duration format." + } + }, + "weekOfYear": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Week of year backup to keep for yearly retention." + } + }, + "yearlyRetention": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Yearly retention in ISO 8601 duration format." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The long-term backup retention policy for the database." + } + }, + "diagnosticSettingFullType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "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 logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "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 both logs & metrics are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the database." + } + }, + "serverName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent SQL Server. Required if the template is used in a standalone deployment." + } + }, + "sku": { + "$ref": "#/definitions/databaseSkuType", + "defaultValue": { + "name": "GP_Gen5_2", + "tier": "GeneralPurpose" + }, + "metadata": { + "description": "Optional. The database SKU." + } + }, + "autoPauseDelay": { + "type": "int", + "defaultValue": -1, + "metadata": { + "description": "Optional. Time in minutes after which database is automatically paused. A value of -1 means that automatic pause is disabled." + } + }, + "availabilityZone": { + "type": "string", + "allowedValues": [ + "1", + "2", + "3", + "NoPreference" + ], + "defaultValue": "NoPreference", + "metadata": { + "description": "Optional. Specifies the availability zone the database is pinned to." + } + }, + "catalogCollation": { + "type": "string", + "defaultValue": "DATABASE_DEFAULT", + "metadata": { + "description": "Optional. Collation of the metadata catalog." + } + }, + "collation": { + "type": "string", + "defaultValue": "SQL_Latin1_General_CP1_CI_AS", + "metadata": { + "description": "Optional. The collation of the database." + } + }, + "createMode": { + "type": "string", + "allowedValues": [ + "Copy", + "Default", + "OnlineSecondary", + "PointInTimeRestore", + "Recovery", + "Restore", + "RestoreExternalBackup", + "RestoreExternalBackupSecondary", + "RestoreLongTermRetentionBackup", + "Secondary" + ], + "defaultValue": "Default", + "metadata": { + "description": "Optional. Specifies the mode of database creation." + } + }, + "elasticPoolResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the elastic pool containing this database." + } + }, + "encryptionProtector": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The azure key vault URI of the database if it's configured with per Database Customer Managed Keys." + } + }, + "encryptionProtectorAutoRotation": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. The flag to enable or disable auto rotation of database encryption protector AKV key." + } + }, + "federatedClientId": { + "type": "string", + "nullable": true, + "minLength": 36, + "maxLength": 36, + "metadata": { + "description": "Optional. The Client id used for cross tenant per database CMK scenario." + } + }, + "freeLimitExhaustionBehavior": { + "type": "string", + "allowedValues": [ + "AutoPause", + "BillOverUsage" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specifies the behavior when monthly free limits are exhausted for the free database." + } + }, + "highAvailabilityReplicaCount": { + "type": "int", + "defaultValue": 0, + "metadata": { + "description": "Optional. The number of readonly secondary replicas associated with the database." + } + }, + "isLedgerOn": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Whether or not this database is a ledger database, which means all tables in the database are ledger tables. Note: the value of this property cannot be changed after the database has been created." + } + }, + "licenseType": { + "type": "string", + "allowedValues": [ + "BasePrice", + "LicenseIncluded" + ], + "nullable": true, + "metadata": { + "description": "Optional. The license type to apply for this database." + } + }, + "longTermRetentionBackupResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource identifier of the long term retention backup associated with create operation of this database." + } + }, + "maintenanceConfigurationId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Maintenance configuration ID assigned to the database. This configuration defines the period when the maintenance updates will occur." + } + }, + "manualCutover": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Whether or not customer controlled manual cutover needs to be done during Update Database operation to Hyperscale tier." + } + }, + "maxSizeBytes": { + "type": "int", + "defaultValue": 34359738368, + "metadata": { + "description": "Optional. The max size of the database expressed in bytes." + } + }, + "minCapacity": { + "type": "string", + "defaultValue": "0", + "metadata": { + "description": "Optional. Minimal capacity that database will always have allocated." + } + }, + "performCutover": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. To trigger customer controlled manual cutover during the wait state while Scaling operation is in progress." + } + }, + "preferredEnclaveType": { + "type": "string", + "allowedValues": [ + "Default", + "VBS" + ], + "nullable": true, + "metadata": { + "description": "Optional. Type of enclave requested on the database i.e. Default or VBS enclaves." + } + }, + "readScale": { + "type": "string", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "defaultValue": "Disabled", + "metadata": { + "description": "Optional. The state of read-only routing." + } + }, + "recoverableDatabaseResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource identifier of the recoverable database associated with create operation of this database." + } + }, + "recoveryServicesRecoveryPointResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource identifier of the recovery point associated with create operation of this database." + } + }, + "requestedBackupStorageRedundancy": { + "type": "string", + "allowedValues": [ + "Geo", + "GeoZone", + "Local", + "Zone" + ], + "defaultValue": "Local", + "metadata": { + "description": "Optional. The storage account type to be used to store backups for this database." + } + }, + "restorableDroppedDatabaseResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource identifier of the restorable dropped database associated with create operation of this database." + } + }, + "restorePointInTime": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Point in time (ISO8601 format) of the source database to restore when createMode set to Restore or PointInTimeRestore." + } + }, + "sampleName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The name of the sample schema to apply when creating this database." + } + }, + "secondaryType": { + "type": "string", + "allowedValues": [ + "Geo", + "Named", + "Standby" + ], + "nullable": true, + "metadata": { + "description": "Optional. The secondary type of the database if it is a secondary." + } + }, + "sourceDatabaseDeletionDate": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The time that the database was deleted when restoring a deleted database." + } + }, + "sourceDatabaseResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource identifier of the source database associated with create operation of this database." + } + }, + "sourceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource identifier of the source associated with the create operation of this database." + } + }, + "useFreeLimit": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Whether or not the database uses free monthly limits. Allowed on one database in a subscription." + } + }, + "zoneRedundant": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Whether or not this database is zone redundant." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "diagnosticSettings": { + "type": "array", + "items": { + "$ref": "#/definitions/diagnosticSettingFullType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "backupShortTermRetentionPolicy": { + "$ref": "#/definitions/shortTermBackupRetentionPolicyType", + "nullable": true, + "metadata": { + "description": "Optional. The short term backup retention policy to create for the database." + } + }, + "backupLongTermRetentionPolicy": { + "$ref": "#/definitions/longTermBackupRetentionPolicyType", + "nullable": true, + "metadata": { + "description": "Optional. The long term backup retention policy to create for the database." + } + } + }, + "resources": { + "server": { + "existing": true, + "type": "Microsoft.Sql/servers", + "apiVersion": "2023-08-01-preview", + "name": "[parameters('serverName')]" + }, + "database": { + "type": "Microsoft.Sql/servers/databases", + "apiVersion": "2023-08-01-preview", + "name": "[format('{0}/{1}', parameters('serverName'), parameters('name'))]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "sku": "[parameters('sku')]", + "properties": { + "autoPauseDelay": "[parameters('autoPauseDelay')]", + "availabilityZone": "[parameters('availabilityZone')]", + "catalogCollation": "[parameters('catalogCollation')]", + "collation": "[parameters('collation')]", + "createMode": "[parameters('createMode')]", + "elasticPoolId": "[parameters('elasticPoolResourceId')]", + "encryptionProtector": "[parameters('encryptionProtector')]", + "encryptionProtectorAutoRotation": "[parameters('encryptionProtectorAutoRotation')]", + "federatedClientId": "[parameters('federatedClientId')]", + "freeLimitExhaustionBehavior": "[parameters('freeLimitExhaustionBehavior')]", + "highAvailabilityReplicaCount": "[parameters('highAvailabilityReplicaCount')]", + "isLedgerOn": "[parameters('isLedgerOn')]", + "licenseType": "[parameters('licenseType')]", + "longTermRetentionBackupResourceId": "[parameters('longTermRetentionBackupResourceId')]", + "maintenanceConfigurationId": "[parameters('maintenanceConfigurationId')]", + "manualCutover": "[parameters('manualCutover')]", + "maxSizeBytes": "[parameters('maxSizeBytes')]", + "minCapacity": "[if(not(empty(parameters('minCapacity'))), json(parameters('minCapacity')), 0)]", + "performCutover": "[parameters('performCutover')]", + "preferredEnclaveType": "[parameters('preferredEnclaveType')]", + "readScale": "[parameters('readScale')]", + "recoverableDatabaseId": "[parameters('recoverableDatabaseResourceId')]", + "recoveryServicesRecoveryPointId": "[parameters('recoveryServicesRecoveryPointResourceId')]", + "requestedBackupStorageRedundancy": "[parameters('requestedBackupStorageRedundancy')]", + "restorableDroppedDatabaseId": "[parameters('restorableDroppedDatabaseResourceId')]", + "restorePointInTime": "[parameters('restorePointInTime')]", + "sampleName": "[parameters('sampleName')]", + "secondaryType": "[parameters('secondaryType')]", + "sourceDatabaseDeletionDate": "[parameters('sourceDatabaseDeletionDate')]", + "sourceDatabaseId": "[parameters('sourceDatabaseResourceId')]", + "sourceResourceId": "[parameters('sourceResourceId')]", + "useFreeLimit": "[parameters('useFreeLimit')]", + "zoneRedundant": "[parameters('zoneRedundant')]" + } + }, + "database_diagnosticSettings": { + "copy": { + "name": "database_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Sql/servers/{0}/databases/{1}', parameters('serverName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null + } + }, + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "database" + ] + }, + "database_backupShortTermRetentionPolicy": { + "condition": "[not(empty(parameters('backupShortTermRetentionPolicy')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-{1}-shBakRetPol', uniqueString(deployment().name, parameters('location')), parameters('name'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "serverName": { + "value": "[parameters('serverName')]" + }, + "databaseName": { + "value": "[parameters('name')]" + }, + "diffBackupIntervalInHours": { + "value": "[tryGet(parameters('backupShortTermRetentionPolicy'), 'diffBackupIntervalInHours')]" + }, + "retentionDays": { + "value": "[tryGet(parameters('backupShortTermRetentionPolicy'), 'retentionDays')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.34.44.8038", + "templateHash": "586417749840962852" + }, + "name": "Azure SQL Server Database Short Term Backup Retention Policies", + "description": "This module deploys an Azure SQL Server Database Short-Term Backup Retention Policy." + }, + "parameters": { + "serverName": { + "type": "string", + "metadata": { + "description": "Required. The name of the parent SQL Server." + } + }, + "databaseName": { + "type": "string", + "metadata": { + "description": "Required. The name of the parent database." + } + }, + "diffBackupIntervalInHours": { + "type": "int", + "defaultValue": 24, + "metadata": { + "description": "Optional. Differential backup interval in hours. For Hyperscal tiers this value will be ignored." + } + }, + "retentionDays": { + "type": "int", + "defaultValue": 7, + "metadata": { + "description": "Optional. Poin-in-time retention in days." + } + } + }, + "resources": [ + { + "type": "Microsoft.Sql/servers/databases/backupShortTermRetentionPolicies", + "apiVersion": "2023-08-01-preview", + "name": "[format('{0}/{1}/{2}', parameters('serverName'), parameters('databaseName'), 'default')]", + "properties": { + "diffBackupIntervalInHours": "[if(equals(reference(resourceId('Microsoft.Sql/servers/databases', parameters('serverName'), parameters('databaseName')), '2023-08-01-preview', 'full').sku.tier, 'Hyperscale'), null(), parameters('diffBackupIntervalInHours'))]", + "retentionDays": "[parameters('retentionDays')]" + } + } + ], + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the short-term policy was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the short-term policy." + }, + "value": "default" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the short-term policy." + }, + "value": "[resourceId('Microsoft.Sql/servers/databases/backupShortTermRetentionPolicies', parameters('serverName'), parameters('databaseName'), 'default')]" + } + } + } + }, + "dependsOn": [ + "database" + ] + }, + "database_backupLongTermRetentionPolicy": { + "condition": "[not(empty(parameters('backupLongTermRetentionPolicy')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-{1}-lgBakRetPol', uniqueString(deployment().name, parameters('location')), parameters('name'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "serverName": { + "value": "[parameters('serverName')]" + }, + "databaseName": { + "value": "[parameters('name')]" + }, + "backupStorageAccessTier": { + "value": "[tryGet(parameters('backupLongTermRetentionPolicy'), 'backupStorageAccessTier')]" + }, + "makeBackupsImmutable": { + "value": "[tryGet(parameters('backupLongTermRetentionPolicy'), 'makeBackupsImmutable')]" + }, + "weeklyRetention": { + "value": "[tryGet(parameters('backupLongTermRetentionPolicy'), 'weeklyRetention')]" + }, + "monthlyRetention": { + "value": "[tryGet(parameters('backupLongTermRetentionPolicy'), 'monthlyRetention')]" + }, + "yearlyRetention": { + "value": "[tryGet(parameters('backupLongTermRetentionPolicy'), 'yearlyRetention')]" + }, + "weekOfYear": { + "value": "[tryGet(parameters('backupLongTermRetentionPolicy'), 'weekOfYear')]" + } + }, + "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.34.44.8038", + "templateHash": "15078686386861189890" + }, + "name": "SQL Server Database Long Term Backup Retention Policies", + "description": "This module deploys an Azure SQL Server Database Long-Term Backup Retention Policy." + }, + "parameters": { + "serverName": { + "type": "string", + "metadata": { + "description": "Required. The name of the parent SQL Server." + } + }, + "databaseName": { + "type": "string", + "metadata": { + "description": "Required. The name of the parent database." + } + }, + "backupStorageAccessTier": { + "type": "string", + "allowedValues": [ + "Archive", + "Hot" + ], + "nullable": true, + "metadata": { + "description": "Optional. The BackupStorageAccessTier for the LTR backups." + } + }, + "makeBackupsImmutable": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. The setting whether to make LTR backups immutable." + } + }, + "monthlyRetention": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Monthly retention in ISO 8601 duration format." + } + }, + "weeklyRetention": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Weekly retention in ISO 8601 duration format." + } + }, + "weekOfYear": { + "type": "int", + "defaultValue": 1, + "metadata": { + "description": "Optional. Week of year backup to keep for yearly retention." + } + }, + "yearlyRetention": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Yearly retention in ISO 8601 duration format." + } + } + }, + "resources": { + "server::database": { + "existing": true, + "type": "Microsoft.Sql/servers/databases", + "apiVersion": "2023-08-01-preview", + "name": "[format('{0}/{1}', parameters('serverName'), parameters('databaseName'))]" + }, + "server": { + "existing": true, + "type": "Microsoft.Sql/servers", + "apiVersion": "2023-08-01-preview", + "name": "[parameters('serverName')]" + }, + "backupLongTermRetentionPolicy": { + "type": "Microsoft.Sql/servers/databases/backupLongTermRetentionPolicies", + "apiVersion": "2023-05-01-preview", + "name": "[format('{0}/{1}/{2}', parameters('serverName'), parameters('databaseName'), 'default')]", + "properties": { + "backupStorageAccessTier": "[parameters('backupStorageAccessTier')]", + "makeBackupsImmutable": "[parameters('makeBackupsImmutable')]", + "monthlyRetention": "[parameters('monthlyRetention')]", + "weeklyRetention": "[parameters('weeklyRetention')]", + "weekOfYear": "[parameters('weekOfYear')]", + "yearlyRetention": "[parameters('yearlyRetention')]" + } + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the long-term policy was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the long-term policy." + }, + "value": "default" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the long-term policy." + }, + "value": "[resourceId('Microsoft.Sql/servers/databases/backupLongTermRetentionPolicies', parameters('serverName'), parameters('databaseName'), 'default')]" + } + } + } + }, + "dependsOn": [ + "database" + ] } }, - "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'." + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed database." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed database." + }, + "value": "[resourceId('Microsoft.Sql/servers/databases', parameters('serverName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed database." + }, + "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('database', '2023-08-01-preview', 'full').location]" } + } + } + }, + "dependsOn": [ + "server", + "server_elasticPools" + ] + }, + "server_elasticPools": { + "copy": { + "name": "server_elasticPools", + "count": "[length(parameters('elasticPools'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-SQLServer-ElasticPool-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "serverName": { + "value": "[parameters('name')]" }, - "principalId": { - "type": "string", - "metadata": { - "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." - } + "location": { + "value": "[parameters('location')]" }, - "principalType": { - "type": "string", - "allowedValues": [ - "Device", - "ForeignGroup", - "Group", - "ServicePrincipal", - "User" - ], - "nullable": true, - "metadata": { - "description": "Optional. The principal type of the assigned principal ID." - } + "tags": { + "value": "[coalesce(tryGet(parameters('elasticPools')[copyIndex()], 'tags'), parameters('tags'))]" }, - "description": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The description of the role assignment." + "name": { + "value": "[parameters('elasticPools')[copyIndex()].name]" + }, + "sku": { + "value": "[tryGet(parameters('elasticPools')[copyIndex()], 'sku')]" + }, + "autoPauseDelay": { + "value": "[tryGet(parameters('elasticPools')[copyIndex()], 'autoPauseDelay')]" + }, + "availabilityZone": { + "value": "[tryGet(parameters('elasticPools')[copyIndex()], 'availabilityZone')]" + }, + "highAvailabilityReplicaCount": { + "value": "[tryGet(parameters('elasticPools')[copyIndex()], 'highAvailabilityReplicaCount')]" + }, + "licenseType": { + "value": "[tryGet(parameters('elasticPools')[copyIndex()], 'licenseType')]" + }, + "maintenanceConfigurationId": { + "value": "[tryGet(parameters('elasticPools')[copyIndex()], 'maintenanceConfigurationId')]" + }, + "maxSizeBytes": { + "value": "[tryGet(parameters('elasticPools')[copyIndex()], 'maxSizeBytes')]" + }, + "minCapacity": { + "value": "[tryGet(parameters('elasticPools')[copyIndex()], 'minCapacity')]" + }, + "perDatabaseSettings": { + "value": "[tryGet(parameters('elasticPools')[copyIndex()], 'perDatabaseSettings')]" + }, + "preferredEnclaveType": { + "value": "[tryGet(parameters('elasticPools')[copyIndex()], 'preferredEnclaveType')]" + }, + "zoneRedundant": { + "value": "[tryGet(parameters('elasticPools')[copyIndex()], 'zoneRedundant')]" + } + }, + "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.34.44.8038", + "templateHash": "15648031842218670048" + }, + "name": "SQL Server Elastic Pool", + "description": "This module deploys an Azure SQL Server Elastic Pool." + }, + "definitions": { + "elasticPoolPerDatabaseSettingsType": { + "type": "object", + "properties": { + "autoPauseDelay": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Auto Pause Delay for per database within pool." + } + }, + "maxCapacity": { + "type": "string", + "metadata": { + "description": "Required. The maximum capacity any one database can consume. Examples: '0.5', '2'." + } + }, + "minCapacity": { + "type": "string", + "metadata": { + "description": "Required. The minimum capacity all databases are guaranteed. Examples: '0.5', '1'." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The per database settings for the elastic pool." + } + }, + "elasticPoolSkuType": { + "type": "object", + "properties": { + "capacity": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The capacity of the particular SKU." + } + }, + "family": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. If the service has different generations of hardware, for the same SKU, then that can be captured here." + } + }, + "name": { + "type": "string", + "allowedValues": [ + "BC_DC", + "BC_Gen5", + "BasicPool", + "GP_DC", + "GP_FSv2", + "GP_Gen5", + "HS_Gen5", + "HS_MOPRMS", + "HS_PRMS", + "PremiumPool", + "ServerlessPool", + "StandardPool" + ], + "metadata": { + "description": "Required. The name of the SKU, typically, a letter + Number code, e.g. P3." + } + }, + "size": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Size of the particular SKU." + } + }, + "tier": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The tier or edition of the particular SKU, e.g. Basic, Premium." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The elastic pool SKU." + } } }, - "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\"." + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the Elastic Pool." + } + }, + "serverName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent SQL Server. Required if the template is used in a standalone deployment." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "sku": { + "$ref": "#/definitions/elasticPoolSkuType", + "defaultValue": { + "capacity": 2, + "name": "GP_Gen5", + "tier": "GeneralPurpose" + }, + "metadata": { + "description": "Optional. The elastic pool SKU." + } + }, + "autoPauseDelay": { + "type": "int", + "defaultValue": -1, + "metadata": { + "description": "Optional. Time in minutes after which elastic pool is automatically paused. A value of -1 means that automatic pause is disabled." + } + }, + "availabilityZone": { + "type": "string", + "allowedValues": [ + "1", + "2", + "3", + "NoPreference" + ], + "defaultValue": "NoPreference", + "metadata": { + "description": "Optional. Specifies the availability zone the pool's primary replica is pinned to." + } + }, + "highAvailabilityReplicaCount": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The number of secondary replicas associated with the elastic pool that are used to provide high availability. Applicable only to Hyperscale elastic pools." + } + }, + "licenseType": { + "type": "string", + "defaultValue": "LicenseIncluded", + "allowedValues": [ + "BasePrice", + "LicenseIncluded" + ], + "metadata": { + "description": "Optional. The license type to apply for this elastic pool." + } + }, + "maintenanceConfigurationId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Maintenance configuration resource ID assigned to the elastic pool. This configuration defines the period when the maintenance updates will will occur." + } + }, + "maxSizeBytes": { + "type": "int", + "defaultValue": 34359738368, + "metadata": { + "description": "Optional. The storage limit for the database elastic pool in bytes." + } + }, + "minCapacity": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Minimal capacity that serverless pool will not shrink below, if not paused." + } + }, + "perDatabaseSettings": { + "$ref": "#/definitions/elasticPoolPerDatabaseSettingsType", + "defaultValue": { + "autoPauseDelay": -1, + "maxCapacity": "2", + "minCapacity": "0" + }, + "metadata": { + "description": "Optional. The per database settings for the elastic pool." + } + }, + "preferredEnclaveType": { + "type": "string", + "allowedValues": [ + "Default", + "VBS" + ], + "defaultValue": "Default", + "metadata": { + "description": "Optional. Type of enclave requested on the elastic pool." + } + }, + "zoneRedundant": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Whether or not this elastic pool is zone redundant, which means the replicas of this elastic pool will be spread across multiple availability zones." + } } }, - "conditionVersion": { - "type": "string", - "allowedValues": [ - "2.0" - ], - "nullable": true, - "metadata": { - "description": "Optional. Version of the condition." + "resources": { + "server": { + "existing": true, + "type": "Microsoft.Sql/servers", + "apiVersion": "2023-08-01-preview", + "name": "[parameters('serverName')]" + }, + "elasticPool": { + "type": "Microsoft.Sql/servers/elasticPools", + "apiVersion": "2023-08-01-preview", + "name": "[format('{0}/{1}', parameters('serverName'), parameters('name'))]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "sku": "[parameters('sku')]", + "properties": { + "autoPauseDelay": "[parameters('autoPauseDelay')]", + "availabilityZone": "[parameters('availabilityZone')]", + "highAvailabilityReplicaCount": "[parameters('highAvailabilityReplicaCount')]", + "licenseType": "[parameters('licenseType')]", + "maintenanceConfigurationId": "[parameters('maintenanceConfigurationId')]", + "maxSizeBytes": "[parameters('maxSizeBytes')]", + "minCapacity": "[parameters('minCapacity')]", + "perDatabaseSettings": "[if(not(empty(parameters('perDatabaseSettings'))), createObject('autoPauseDelay', tryGet(parameters('perDatabaseSettings'), 'autoPauseDelay'), 'maxCapacity', json(tryGet(parameters('perDatabaseSettings'), 'maxCapacity')), 'minCapacity', json(tryGet(parameters('perDatabaseSettings'), 'minCapacity'))), null())]", + "preferredEnclaveType": "[parameters('preferredEnclaveType')]", + "zoneRedundant": "[parameters('zoneRedundant')]" + } } }, - "delegatedManagedIdentityResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The Resource Id of the delegated managed identity resource." + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed Elastic Pool." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed Elastic Pool." + }, + "value": "[resourceId('Microsoft.Sql/servers/elasticPools', parameters('serverName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed Elastic Pool." + }, + "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('elasticPool', '2023-08-01-preview', 'full').location]" } } } }, - "nullable": true + "dependsOn": [ + "server" + ] }, - "lockType": { - "type": "object", + "server_privateEndpoints": { + "copy": { + "name": "server_privateEndpoints", + "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-server-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]]", "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Specify the name of lock." - } + "expressionEvaluationOptions": { + "scope": "inner" }, - "kind": { - "type": "string", - "allowedValues": [ - "CanNotDelete", - "None", - "ReadOnly" - ], - "nullable": true, - "metadata": { - "description": "Optional. Specify the type of lock." - } - } - }, - "nullable": true - }, - "aType": { - "type": "array", - "items": { - "type": "object", - "properties": { + "mode": "Incremental", + "parameters": { "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'name'), format('pep-{0}-{1}-{2}', last(split(resourceId('Microsoft.Sql/servers', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'sqlServer'), copyIndex()))]" }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } + "privateLinkServiceConnections": "[if(not(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true())), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.Sql/servers', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'sqlServer'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.Sql/servers', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'sqlServer')))))), createObject('value', null()))]", + "manualPrivateLinkServiceConnections": "[if(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true()), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.Sql/servers', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'sqlServer'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.Sql/servers', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'sqlServer')), 'requestMessage', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualConnectionRequestMessage'), 'Manual approval required.'))))), createObject('value', null()))]", + "subnetResourceId": { + "value": "[coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId]" }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } + "enableTelemetry": { + "value": "[variables('enableReferencedModulesTelemetry')]" }, - "aRecords": { - "type": "array", - "items": { - "type": "object", - "properties": { - "ipv4Address": { - "type": "string", - "metadata": { - "description": "Required. The IPv4 address of this A record." - } - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. The list of A records in the record set." - } - } - } - }, - "nullable": true - }, - "aaaaType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } + "location": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'location'), reference(split(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location)]" }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } + "lock": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } + "privateDnsZoneGroup": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroup')]" }, "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'roleAssignments')]" }, - "aaaaRecords": { - "type": "array", - "items": { - "type": "object", - "properties": { - "ipv6Address": { - "type": "string", - "metadata": { - "description": "Required. The IPv6 address of this AAAA record." - } - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. The list of AAAA records in the record set." - } - } - } - }, - "nullable": true - }, - "cnameType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } + "tags": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } + "customDnsConfigs": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customDnsConfigs')]" }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } + "ipConfigurations": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'ipConfigurations')]" }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } + "applicationSecurityGroupResourceIds": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'applicationSecurityGroupResourceIds')]" }, - "cnameRecord": { - "type": "object", - "properties": { - "cname": { - "type": "string", - "metadata": { - "description": "Required. The canonical name of the CNAME record." - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. The CNAME record in the record set." - } + "customNetworkInterfaceName": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customNetworkInterfaceName')]" } - } - }, - "nullable": true - }, - "mxType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } - }, + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } - }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } + "_generator": { + "name": "bicep", + "version": "0.33.13.18514", + "templateHash": "15954548978129725136" + }, + "name": "Private Endpoints", + "description": "This module deploys a Private Endpoint." }, - "mxRecords": { - "type": "array", - "items": { + "definitions": { + "privateDnsZoneGroupType": { "type": "object", "properties": { - "exchange": { + "name": { "type": "string", + "nullable": true, "metadata": { - "description": "Required. The domain name of the mail host for this MX record." + "description": "Optional. The name of the Private DNS Zone Group." } }, - "preference": { - "type": "int", + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, "metadata": { - "description": "Required. The preference value for this MX record." + "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." } } + }, + "metadata": { + "__bicep_export!": true } }, - "nullable": true, - "metadata": { - "description": "Optional. The list of MX records in the record set." - } - } - } - }, - "nullable": true - }, - "ptrType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } - }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "ptrRecords": { - "type": "array", - "items": { + "ipConfigurationType": { "type": "object", "properties": { - "ptrdname": { + "name": { "type": "string", "metadata": { - "description": "Required. The PTR target domain name for this PTR record." + "description": "Required. The name of the resource that is unique within a resource group." + } + }, + "properties": { + "type": "object", + "properties": { + "groupId": { + "type": "string", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } + } + }, + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." } } + }, + "metadata": { + "__bicep_export!": true } }, - "nullable": true, - "metadata": { - "description": "Optional. The list of PTR records in the record set." - } - } - } - }, - "nullable": true - }, - "soaType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } - }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "soaRecord": { - "type": "object", - "properties": { - "email": { - "type": "string", - "metadata": { - "description": "Required. The email contact for this SOA record." - } - }, - "expireTime": { - "type": "int", - "metadata": { - "description": "Required. The expire time for this SOA record." + "privateLinkServiceConnectionType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the private link service connection." + } + }, + "properties": { + "type": "object", + "properties": { + "groupIds": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." + } + }, + "privateLinkServiceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of private link service." + } + }, + "requestMessage": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." + } + } + }, + "metadata": { + "description": "Required. Properties of private link service connection." + } } }, - "host": { - "type": "string", - "metadata": { - "description": "Required. The domain name of the authoritative name server for this SOA record." + "metadata": { + "__bicep_export!": true + } + }, + "customDnsConfigType": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. FQDN that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." + } } }, - "minimumTtl": { - "type": "int", - "metadata": { - "description": "Required. The minimum value for this SOA record. By convention this is used to determine the negative caching duration." + "metadata": { + "__bicep_export!": true + } + }, + "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." + } } }, - "refreshTime": { - "type": "int", - "metadata": { - "description": "Required. The refresh value for this SOA record." + "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.5.1" } - }, - "retryTime": { - "type": "int", - "metadata": { - "description": "Required. The retry time for this SOA record." + } + }, + "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." + } } }, - "serialNumber": { - "type": "int", - "metadata": { - "description": "Required. The serial number for this SOA record." + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "private-dns-zone-group/main.bicep" } } }, - "nullable": true, - "metadata": { - "description": "Optional. The SOA record in the record set." - } - } - } - }, - "nullable": true - }, - "srvType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." - } - }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "srvRecords": { - "type": "array", - "items": { + "roleAssignmentType": { "type": "object", "properties": { - "priority": { - "type": "int", + "name": { + "type": "string", + "nullable": true, "metadata": { - "description": "Required. The priority value for this SRV record." + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." } }, - "weight": { - "type": "int", + "roleDefinitionIdOrName": { + "type": "string", "metadata": { - "description": "Required. The weight value for this SRV record." + "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'." } }, - "port": { - "type": "int", + "principalId": { + "type": "string", "metadata": { - "description": "Required. The port value for this SRV record." + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." } }, - "target": { + "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": "Required. The target domain name for this SRV record." + "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.5.1" + } } - }, - "nullable": true, - "metadata": { - "description": "Optional. The list of SRV records in the record set." - } - } - } - }, - "nullable": true - }, - "txtType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the record." - } - }, - "metadata": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. The metadata of the record." } }, - "ttl": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The TTL of the record." + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the private endpoint resource to create." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + } + }, + "applicationSecurityGroupResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Application security groups in which the private endpoint IP configuration is included." + } + }, + "customNetworkInterfaceName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The custom name of the network interface attached to the private endpoint." + } + }, + "ipConfigurations": { + "type": "array", + "items": { + "$ref": "#/definitions/ipConfigurationType" + }, + "nullable": true, + "metadata": { + "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." + } + }, + "privateDnsZoneGroup": { + "$ref": "#/definitions/privateDnsZoneGroupType", + "nullable": true, + "metadata": { + "description": "Optional. The private DNS zone group to configure for the private endpoint." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "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", + "nullable": true, + "metadata": { + "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/customDnsConfigType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Custom DNS configurations." + } + }, + "manualPrivateLinkServiceConnections": { + "type": "array", + "items": { + "$ref": "#/definitions/privateLinkServiceConnectionType" + }, + "nullable": true, + "metadata": { + "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." + } + }, + "privateLinkServiceConnections": { + "type": "array", + "items": { + "$ref": "#/definitions/privateLinkServiceConnectionType" + }, + "nullable": true, + "metadata": { + "description": "Conditional. A grouping of information about the connection to the remote resource. Required if `manualPrivateLinkServiceConnections` is empty." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } } }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." + "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')]" } }, - "txtRecords": { - "type": "array", - "items": { - "type": "object", + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.10.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { - "value": { - "type": "array", - "items": { - "type": "string" + "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": "2023-11-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": "[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": "2022-09-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.33.13.18514", + "templateHash": "5440815542537978381" + }, + "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 + } + } }, - "metadata": { - "description": "Required. The text value of this TXT record." + "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." + } + } + }, + "variables": { + "copy": [ + { + "name": "privateDnsZoneConfigsVar", + "count": "[length(parameters('privateDnsZoneConfigs'))]", + "input": { + "name": "[coalesce(tryGet(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')], 'name'), last(split(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId, '/')))]", + "properties": { + "privateDnsZoneId": "[parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId]" + } + } + } + ] + }, + "resources": { + "privateEndpoint": { + "existing": true, + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-11-01", + "name": "[parameters('privateEndpointName')]" + }, + "privateDnsZoneGroup": { + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2023-11-01", + "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", + "properties": { + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" + } + } + }, + "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]" + } } } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. The list of TXT records in the record set." - } - } - } - }, - "nullable": true - }, - "virtualNetworkLinkType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "minLength": 1, - "maxLength": 80, - "metadata": { - "description": "Optional. The resource name." - } - }, - "virtualNetworkResourceId": { - "type": "string", - "metadata": { - "description": "Required. The resource ID of the virtual network to link." - } - }, - "location": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The Azure Region where the resource lives." - } - }, - "registrationEnabled": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Is auto-registration of virtual machine records in the virtual network in the Private DNS zone enabled?." - } - }, - "tags": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Resource tags." + }, + "dependsOn": [ + "privateEndpoint" + ] } }, - "resolutionPolicy": { - "type": "string", - "allowedValues": [ - "Default", - "NxDomainRedirect" - ], - "nullable": true, - "metadata": { - "description": "Optional. The resolution type of the private-dns-zone fallback machanism." - } - } - } - }, - "nullable": true - } - }, - "parameters": { - "name": { - "type": "string", - "metadata": { - "description": "Required. Private DNS zone name." - } - }, - "a": { - "$ref": "#/definitions/aType", - "metadata": { - "description": "Optional. Array of A records." - } - }, - "aaaa": { - "$ref": "#/definitions/aaaaType", - "metadata": { - "description": "Optional. Array of AAAA records." - } - }, - "cname": { - "$ref": "#/definitions/cnameType", - "metadata": { - "description": "Optional. Array of CNAME records." - } - }, - "mx": { - "$ref": "#/definitions/mxType", - "metadata": { - "description": "Optional. Array of MX records." - } - }, - "ptr": { - "$ref": "#/definitions/ptrType", - "metadata": { - "description": "Optional. Array of PTR records." - } - }, - "soa": { - "$ref": "#/definitions/soaType", - "metadata": { - "description": "Optional. Array of SOA records." - } - }, - "srv": { - "$ref": "#/definitions/srvType", - "metadata": { - "description": "Optional. Array of SRV records." - } - }, - "txt": { - "$ref": "#/definitions/txtType", - "metadata": { - "description": "Optional. Array of TXT records." - } - }, - "virtualNetworkLinks": { - "$ref": "#/definitions/virtualNetworkLinkType", - "metadata": { - "description": "Optional. Array of custom objects describing vNet links of the DNS zone. Each object should contain properties 'virtualNetworkResourceId' and 'registrationEnabled'. The 'vnetResourceId' is a resource ID of a vNet to link, 'registrationEnabled' (bool) enables automatic DNS registration in the zone for the linked vNet." - } - }, - "location": { - "type": "string", - "defaultValue": "global", - "metadata": { - "description": "Optional. The location of the PrivateDNSZone. Should be global." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "tags": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Tags of the resource." - } - }, - "lock": { - "$ref": "#/definitions/lockType", - "metadata": { - "description": "Optional. The lock settings of the service." - } - }, - "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')]", - "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": "2024-03-01", - "name": "[format('46d3xbcp.res.network-privatednszone.{0}.{1}', replace('0.7.0', '.', '-'), 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" + "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', '2023-11-01', 'full').location]" + }, + "customDnsConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/customDnsConfigType" + }, + "metadata": { + "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))]" } } } - } - }, - "privateDnsZone": { - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('name')]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]" - }, - "privateDnsZone_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/privateDnsZones/{0}', parameters('name'))]", - "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", - "properties": { - "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", - "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" - }, - "dependsOn": [ - "privateDnsZone" - ] - }, - "privateDnsZone_roleAssignments": { - "copy": { - "name": "privateDnsZone_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}', parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones', 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": [ - "privateDnsZone" + "server" ] }, - "privateDnsZone_A": { + "server_firewallRules": { "copy": { - "name": "privateDnsZone_A", - "count": "[length(coalesce(parameters('a'), createArray()))]" + "name": "server_firewallRules", + "count": "[length(parameters('firewallRules'))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-ARecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "name": "[format('{0}-Sql-FirewallRules-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "privateDnsZoneName": { - "value": "[parameters('name')]" - }, "name": { - "value": "[coalesce(parameters('a'), createArray())[copyIndex()].name]" - }, - "aRecords": { - "value": "[tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'aRecords')]" + "value": "[parameters('firewallRules')[copyIndex()].name]" }, - "metadata": { - "value": "[tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'metadata')]" + "serverName": { + "value": "[parameters('name')]" }, - "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'ttl'), 3600)]" + "endIpAddress": { + "value": "[tryGet(parameters('firewallRules')[copyIndex()], 'endIpAddress')]" }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'roleAssignments')]" + "startIpAddress": { + "value": "[tryGet(parameters('firewallRules')[copyIndex()], 'startIpAddress')]" } }, "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.32.4.45862", - "templateHash": "2531120132215940282" - }, - "name": "Private DNS Zone A record", - "description": "This module deploys a Private DNS Zone A record.", - "owner": "Azure/module-maintainers" - }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } - } - } - }, - "nullable": true - } - }, - "parameters": { - "privateDnsZoneName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." - } + "version": "0.34.44.8038", + "templateHash": "7783790094003665329" }, + "name": "Azure SQL Server Firewall Rule", + "description": "This module deploys an Azure SQL Server Firewall Rule." + }, + "parameters": { "name": { "type": "string", "metadata": { - "description": "Required. The name of the A record." - } - }, - "aRecords": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. The list of A records in the record set." + "description": "Required. The name of the Server Firewall Rule." } }, - "metadata": { - "type": "object", - "nullable": true, + "endIpAddress": { + "type": "string", + "defaultValue": "0.0.0.0", "metadata": { - "description": "Optional. The metadata attached to the record set." + "description": "Optional. The end IP address of the firewall rule. Must be IPv4 format. Must be greater than or equal to startIpAddress. Use value '0.0.0.0' for all Azure-internal IP addresses." } }, - "ttl": { - "type": "int", - "defaultValue": 3600, + "startIpAddress": { + "type": "string", + "defaultValue": "0.0.0.0", "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." + "description": "Optional. The start IP address of the firewall rule. Must be IPv4 format. Use value '0.0.0.0' for all Azure-internal IP addresses." } }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", + "serverName": { + "type": "string", "metadata": { - "description": "Optional. Array of role assignments to create." - } - } - }, - "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)))))]" + "description": "Conditional. The name of the parent SQL Server. Required if the template is used in a standalone deployment." } - ], - "builtInRoleNames": { - "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", - "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, - "resources": { - "privateDnsZone": { - "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" - }, - "A": { - "type": "Microsoft.Network/privateDnsZones/A", - "apiVersion": "2020-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "resources": [ + { + "type": "Microsoft.Sql/servers/firewallRules", + "apiVersion": "2023-08-01-preview", + "name": "[format('{0}/{1}', parameters('serverName'), parameters('name'))]", "properties": { - "aRecords": "[parameters('aRecords')]", - "metadata": "[parameters('metadata')]", - "ttl": "[parameters('ttl')]" + "endIpAddress": "[parameters('endIpAddress')]", + "startIpAddress": "[parameters('startIpAddress')]" } - }, - "A_roleAssignments": { - "copy": { - "name": "A_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/A/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/A', parameters('privateDnsZoneName'), 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": [ - "A" - ] } - }, + ], "outputs": { "name": { "type": "string", "metadata": { - "description": "The name of the deployed A record." + "description": "The name of the deployed firewall rule." }, "value": "[parameters('name')]" }, "resourceId": { "type": "string", "metadata": { - "description": "The resource ID of the deployed A record." + "description": "The resource ID of the deployed firewall rule." }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/A', parameters('privateDnsZoneName'), parameters('name'))]" + "value": "[resourceId('Microsoft.Sql/servers/firewallRules', parameters('serverName'), parameters('name'))]" }, "resourceGroupName": { "type": "string", "metadata": { - "description": "The resource group of the deployed A record." + "description": "The resource group of the deployed firewall rule." }, "value": "[resourceGroup().name]" } @@ -78907,249 +90940,105 @@ } }, "dependsOn": [ - "privateDnsZone" + "server" ] }, - "privateDnsZone_AAAA": { + "server_virtualNetworkRules": { "copy": { - "name": "privateDnsZone_AAAA", - "count": "[length(coalesce(parameters('aaaa'), createArray()))]" + "name": "server_virtualNetworkRules", + "count": "[length(parameters('virtualNetworkRules'))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-AAAARecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "name": "[format('{0}-Sql-VirtualNetworkRules-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "privateDnsZoneName": { + "serverName": { "value": "[parameters('name')]" }, "name": { - "value": "[coalesce(parameters('aaaa'), createArray())[copyIndex()].name]" - }, - "aaaaRecords": { - "value": "[tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'aaaaRecords')]" - }, - "metadata": { - "value": "[tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'metadata')]" + "value": "[parameters('virtualNetworkRules')[copyIndex()].name]" }, - "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'ttl'), 3600)]" + "ignoreMissingVnetServiceEndpoint": { + "value": "[tryGet(parameters('virtualNetworkRules')[copyIndex()], 'ignoreMissingVnetServiceEndpoint')]" }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'roleAssignments')]" + "virtualNetworkSubnetId": { + "value": "[parameters('virtualNetworkRules')[copyIndex()].virtualNetworkSubnetId]" } }, "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.32.4.45862", - "templateHash": "16709340450244912125" + "version": "0.34.44.8038", + "templateHash": "529105308382514230" }, - "name": "Private DNS Zone AAAA record", - "description": "This module deploys a Private DNS Zone AAAA record.", - "owner": "Azure/module-maintainers" - }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } - } - } - }, - "nullable": true - } + "name": "Azure SQL Server Virtual Network Rules", + "description": "This module deploys an Azure SQL Server Virtual Network Rule." }, "parameters": { - "privateDnsZoneName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." - } - }, "name": { "type": "string", "metadata": { - "description": "Required. The name of the AAAA record." - } - }, - "aaaaRecords": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. The list of AAAA records in the record set." + "description": "Required. The name of the Server Virtual Network Rule." } }, - "metadata": { - "type": "object", - "nullable": true, + "ignoreMissingVnetServiceEndpoint": { + "type": "bool", + "defaultValue": false, "metadata": { - "description": "Optional. The metadata attached to the record set." + "description": "Optional. Allow creating a firewall rule before the virtual network has vnet service endpoint enabled." } }, - "ttl": { - "type": "int", - "defaultValue": 3600, + "virtualNetworkSubnetId": { + "type": "string", "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." + "description": "Required. The resource ID of the virtual network subnet." } }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", + "serverName": { + "type": "string", "metadata": { - "description": "Optional. Array of role assignments to create." - } - } - }, - "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)))))]" + "description": "Conditional. The name of the parent SQL Server. Required if the template is used in a standalone deployment." } - ], - "builtInRoleNames": { - "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", - "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, - "resources": { - "privateDnsZone": { - "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" - }, - "AAAA": { - "type": "Microsoft.Network/privateDnsZones/AAAA", - "apiVersion": "2020-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "resources": [ + { + "type": "Microsoft.Sql/servers/virtualNetworkRules", + "apiVersion": "2023-08-01-preview", + "name": "[format('{0}/{1}', parameters('serverName'), parameters('name'))]", "properties": { - "aaaaRecords": "[parameters('aaaaRecords')]", - "metadata": "[parameters('metadata')]", - "ttl": "[parameters('ttl')]" + "ignoreMissingVnetServiceEndpoint": "[parameters('ignoreMissingVnetServiceEndpoint')]", + "virtualNetworkSubnetId": "[parameters('virtualNetworkSubnetId')]" } - }, - "AAAA_roleAssignments": { - "copy": { - "name": "AAAA_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/AAAA/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/AAAA', parameters('privateDnsZoneName'), 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": [ - "AAAA" - ] } - }, + ], "outputs": { "name": { "type": "string", "metadata": { - "description": "The name of the deployed AAAA record." + "description": "The name of the deployed virtual network rule." }, "value": "[parameters('name')]" }, "resourceId": { "type": "string", "metadata": { - "description": "The resource ID of the deployed AAAA record." + "description": "The resource ID of the deployed virtual network rule." }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/AAAA', parameters('privateDnsZoneName'), parameters('name'))]" + "value": "[resourceId('Microsoft.Sql/servers/virtualNetworkRules', parameters('serverName'), parameters('name'))]" }, "resourceGroupName": { "type": "string", "metadata": { - "description": "The resource group of the deployed AAAA record." + "description": "The resource group of the deployed virtual network rule." }, "value": "[resourceGroup().name]" } @@ -79157,40 +91046,49 @@ } }, "dependsOn": [ - "privateDnsZone" + "server" ] }, - "privateDnsZone_CNAME": { + "server_securityAlertPolicies": { "copy": { - "name": "privateDnsZone_CNAME", - "count": "[length(coalesce(parameters('cname'), createArray()))]" + "name": "server_securityAlertPolicies", + "count": "[length(parameters('securityAlertPolicies'))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-CNAMERecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "name": "[format('{0}-Sql-SecAlertPolicy-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "privateDnsZoneName": { + "name": { + "value": "[parameters('securityAlertPolicies')[copyIndex()].name]" + }, + "serverName": { "value": "[parameters('name')]" }, - "name": { - "value": "[coalesce(parameters('cname'), createArray())[copyIndex()].name]" + "disabledAlerts": { + "value": "[tryGet(parameters('securityAlertPolicies')[copyIndex()], 'disabledAlerts')]" }, - "cnameRecord": { - "value": "[tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'cnameRecord')]" + "emailAccountAdmins": { + "value": "[tryGet(parameters('securityAlertPolicies')[copyIndex()], 'emailAccountAdmins')]" }, - "metadata": { - "value": "[tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'metadata')]" + "emailAddresses": { + "value": "[tryGet(parameters('securityAlertPolicies')[copyIndex()], 'emailAddresses')]" }, - "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'ttl'), 3600)]" + "retentionDays": { + "value": "[tryGet(parameters('securityAlertPolicies')[copyIndex()], 'retentionDays')]" }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'roleAssignments')]" + "state": { + "value": "[tryGet(parameters('securityAlertPolicies')[copyIndex()], 'state')]" + }, + "storageAccountAccessKey": { + "value": "[tryGet(parameters('securityAlertPolicies')[copyIndex()], 'storageAccountAccessKey')]" + }, + "storageEndpoint": { + "value": "[tryGet(parameters('securityAlertPolicies')[copyIndex()], 'storageEndpoint')]" } }, "template": { @@ -79200,206 +91098,134 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "9976020649752073181" + "version": "0.34.44.8038", + "templateHash": "5426873131651303318" }, - "name": "Private DNS Zone CNAME record", - "description": "This module deploys a Private DNS Zone CNAME record.", - "owner": "Azure/module-maintainers" + "name": "Azure SQL Server Security Alert Policies", + "description": "This module deploys an Azure SQL Server Security Alert Policy." }, - "definitions": { - "roleAssignmentType": { + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the Security Alert Policy." + } + }, + "disabledAlerts": { "type": "array", "items": { - "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." - } - } - } + "type": "string" }, - "nullable": true - } - }, - "parameters": { - "privateDnsZoneName": { - "type": "string", + "defaultValue": [], + "allowedValues": [ + "Sql_Injection", + "Sql_Injection_Vulnerability", + "Access_Anomaly", + "Data_Exfiltration", + "Unsafe_Action", + "Brute_Force" + ], "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + "description": "Optional. Alerts to disable." } }, - "name": { - "type": "string", + "emailAccountAdmins": { + "type": "bool", + "defaultValue": false, "metadata": { - "description": "Required. The name of the CNAME record." + "description": "Optional. Specifies that the alert is sent to the account administrators." } }, - "cnameRecord": { - "type": "object", - "nullable": true, + "emailAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "defaultValue": [], "metadata": { - "description": "Optional. A CNAME record." + "description": "Optional. Specifies an array of email addresses to which the alert is sent." } }, - "metadata": { - "type": "object", + "retentionDays": { + "type": "int", + "defaultValue": 0, + "metadata": { + "description": "Optional. Specifies the number of days to keep in the Threat Detection audit logs." + } + }, + "state": { + "type": "string", + "defaultValue": "Disabled", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "metadata": { + "description": "Optional. Specifies the state of the policy, whether it is enabled or disabled or a policy has not been applied yet on the specific database." + } + }, + "storageAccountAccessKey": { + "type": "securestring", "nullable": true, "metadata": { - "description": "Optional. The metadata attached to the record set." + "description": "Optional. Specifies the identifier key of the Threat Detection audit storage account." } }, - "ttl": { - "type": "int", - "defaultValue": 3600, + "storageEndpoint": { + "type": "string", + "nullable": true, "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." + "description": "Optional. Specifies the blob storage endpoint. This blob storage will hold all Threat Detection audit logs." } }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", + "serverName": { + "type": "string", "metadata": { - "description": "Optional. Array of role assignments to create." - } - } - }, - "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)))))]" + "description": "Conditional. The name of the parent SQL Server. Required if the template is used in a standalone deployment." } - ], - "builtInRoleNames": { - "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", - "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, "resources": { - "privateDnsZone": { + "server": { "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" + "type": "Microsoft.Sql/servers", + "apiVersion": "2023-08-01-preview", + "name": "[parameters('serverName')]" }, - "CNAME": { - "type": "Microsoft.Network/privateDnsZones/CNAME", - "apiVersion": "2020-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "securityAlertPolicy": { + "type": "Microsoft.Sql/servers/securityAlertPolicies", + "apiVersion": "2023-08-01-preview", + "name": "[format('{0}/{1}', parameters('serverName'), parameters('name'))]", "properties": { - "cnameRecord": "[parameters('cnameRecord')]", - "metadata": "[parameters('metadata')]", - "ttl": "[parameters('ttl')]" + "disabledAlerts": "[parameters('disabledAlerts')]", + "emailAccountAdmins": "[parameters('emailAccountAdmins')]", + "emailAddresses": "[parameters('emailAddresses')]", + "retentionDays": "[parameters('retentionDays')]", + "state": "[parameters('state')]", + "storageAccountAccessKey": "[parameters('storageAccountAccessKey')]", + "storageEndpoint": "[parameters('storageEndpoint')]" } - }, - "CNAME_roleAssignments": { - "copy": { - "name": "CNAME_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/CNAME/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/CNAME', parameters('privateDnsZoneName'), 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": [ - "CNAME" - ] } }, "outputs": { "name": { "type": "string", "metadata": { - "description": "The name of the deployed CNAME record." + "description": "The name of the deployed security alert policy." }, "value": "[parameters('name')]" }, "resourceId": { "type": "string", "metadata": { - "description": "The resource ID of the deployed CNAME record." + "description": "The resource ID of the deployed security alert policy." }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/CNAME', parameters('privateDnsZoneName'), parameters('name'))]" + "value": "[resourceId('Microsoft.Sql/servers/securityAlertPolicies', parameters('serverName'), parameters('name'))]" }, "resourceGroupName": { "type": "string", "metadata": { - "description": "The resource group of the deployed CNAME record." + "description": "The resource group of the deployed security alert policy." }, "value": "[resourceGroup().name]" } @@ -79407,40 +91233,37 @@ } }, "dependsOn": [ - "privateDnsZone" + "server" ] }, - "privateDnsZone_MX": { - "copy": { - "name": "privateDnsZone_MX", - "count": "[length(coalesce(parameters('mx'), createArray()))]" - }, + "server_vulnerabilityAssessment": { + "condition": "[not(equals(parameters('vulnerabilityAssessmentsObj'), null()))]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-MXRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "name": "[format('{0}-Sql-VulnAssessm', uniqueString(deployment().name, parameters('location')))]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "privateDnsZoneName": { + "serverName": { "value": "[parameters('name')]" }, "name": { - "value": "[coalesce(parameters('mx'), createArray())[copyIndex()].name]" + "value": "[parameters('vulnerabilityAssessmentsObj').name]" }, - "metadata": { - "value": "[tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'metadata')]" + "recurringScans": { + "value": "[tryGet(parameters('vulnerabilityAssessmentsObj'), 'recurringScans')]" }, - "mxRecords": { - "value": "[tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'mxRecords')]" + "storageAccountResourceId": { + "value": "[parameters('vulnerabilityAssessmentsObj').storageAccountResourceId]" }, - "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'ttl'), 3600)]" + "useStorageAccountAccessKey": { + "value": "[tryGet(parameters('vulnerabilityAssessmentsObj'), 'useStorageAccountAccessKey')]" }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'roleAssignments')]" + "createStorageRoleAssignment": { + "value": "[tryGet(parameters('vulnerabilityAssessmentsObj'), 'createStorageRoleAssignment')]" } }, "template": { @@ -79450,184 +91273,162 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "2520323624213076361" + "version": "0.34.44.8038", + "templateHash": "16010734059576789187" }, - "name": "Private DNS Zone MX record", - "description": "This module deploys a Private DNS Zone MX record.", - "owner": "Azure/module-maintainers" + "name": "Azure SQL Server Vulnerability Assessments", + "description": "This module deploys an Azure SQL Server Vulnerability Assessment." }, "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } + "recurringScansType": { + "type": "object", + "properties": { + "emails": { + "type": "array", + "items": { + "type": "string" }, - "delegatedManagedIdentityResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The Resource Id of the delegated managed identity resource." - } + "metadata": { + "description": "Required. Specifies an array of e-mail addresses to which the scan notification is sent." + } + }, + "emailSubscriptionAdmins": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Specifies that the schedule scan notification will be sent to the subscription administrators." + } + }, + "isEnabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Recurring scans state." } } }, - "nullable": true + "metadata": { + "__bicep_export!": true + } } }, "parameters": { - "privateDnsZoneName": { + "name": { "type": "string", "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + "description": "Required. The name of the vulnerability assessment." } }, - "name": { + "serverName": { "type": "string", "metadata": { - "description": "Required. The name of the MX record." + "description": "Conditional. The Name of SQL Server. Required if the template is used in a standalone deployment." } }, - "metadata": { - "type": "object", - "nullable": true, + "recurringScans": { + "$ref": "#/definitions/recurringScansType", + "defaultValue": { + "emails": [], + "emailSubscriptionAdmins": false, + "isEnabled": false + }, "metadata": { - "description": "Optional. The metadata attached to the record set." + "description": "Optional. The recurring scans settings." } }, - "mxRecords": { - "type": "array", - "nullable": true, + "storageAccountResourceId": { + "type": "string", "metadata": { - "description": "Optional. The list of MX records in the record set." + "description": "Required. A blob storage to hold the scan results." } }, - "ttl": { - "type": "int", - "defaultValue": 3600, + "useStorageAccountAccessKey": { + "type": "bool", + "defaultValue": false, "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." + "description": "Optional. Use Access Key to access the storage account. The storage account cannot be behind a firewall or virtual network. If an access key is not used, the SQL Server system assigned managed identity must be assigned the Storage Blob Data Contributor role on the storage account." } }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", + "createStorageRoleAssignment": { + "type": "bool", + "defaultValue": true, "metadata": { - "description": "Optional. Array of role assignments to create." - } - } - }, - "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)))))]" + "description": "Optional. Create the Storage Blob Data Contributor role assignment on the storage account. Note, the role assignment must not already exist on the storage account." } - ], - "builtInRoleNames": { - "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", - "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, "resources": { - "privateDnsZone": { + "server": { "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" + "type": "Microsoft.Sql/servers", + "apiVersion": "2023-08-01-preview", + "name": "[parameters('serverName')]" }, - "MX": { - "type": "Microsoft.Network/privateDnsZones/MX", - "apiVersion": "2020-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "vulnerabilityAssessment": { + "type": "Microsoft.Sql/servers/vulnerabilityAssessments", + "apiVersion": "2023-08-01-preview", + "name": "[format('{0}/{1}', parameters('serverName'), parameters('name'))]", "properties": { - "metadata": "[parameters('metadata')]", - "mxRecords": "[parameters('mxRecords')]", - "ttl": "[parameters('ttl')]" + "storageContainerPath": "[format('https://{0}.blob.{1}/vulnerability-assessment/', last(split(parameters('storageAccountResourceId'), '/')), environment().suffixes.storage)]", + "storageAccountAccessKey": "[if(parameters('useStorageAccountAccessKey'), listKeys(parameters('storageAccountResourceId'), '2019-06-01').keys[0].value, null())]", + "recurringScans": "[parameters('recurringScans')]" } }, - "MX_roleAssignments": { - "copy": { - "name": "MX_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/MX/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/MX', parameters('privateDnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "storageAccount_sbdc_rbac": { + "condition": "[and(not(parameters('useStorageAccountAccessKey')), parameters('createStorageRoleAssignment'))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-sbdc-rbac', parameters('serverName'))]", + "subscriptionId": "[split(parameters('storageAccountResourceId'), '/')[2]]", + "resourceGroup": "[split(parameters('storageAccountResourceId'), '/')[4]]", "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')]" + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "storageAccountName": { + "value": "[last(split(parameters('storageAccountResourceId'), '/'))]" + }, + "managedInstanceIdentityPrincipalId": { + "value": "[reference('server', '2023-08-01-preview', 'full').identity.principalId]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.34.44.8038", + "templateHash": "16931034564891209519" + } + }, + "parameters": { + "storageAccountName": { + "type": "string" + }, + "managedInstanceIdentityPrincipalId": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}', parameters('storageAccountName'))]", + "name": "[guid(format('{0}-{1}-Storage-Blob-Data-Contributor', resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName')), parameters('managedInstanceIdentityPrincipalId')))]", + "properties": { + "roleDefinitionId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe')]", + "principalId": "[parameters('managedInstanceIdentityPrincipalId')]", + "principalType": "ServicePrincipal" + } + } + ] + } }, "dependsOn": [ - "MX" + "server" ] } }, @@ -79635,21 +91436,21 @@ "name": { "type": "string", "metadata": { - "description": "The name of the deployed MX record." + "description": "The name of the deployed vulnerability assessment." }, "value": "[parameters('name')]" }, "resourceId": { "type": "string", "metadata": { - "description": "The resource ID of the deployed MX record." + "description": "The resource ID of the deployed vulnerability assessment." }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/MX', parameters('privateDnsZoneName'), parameters('name'))]" + "value": "[resourceId('Microsoft.Sql/servers/vulnerabilityAssessments', parameters('serverName'), parameters('name'))]" }, "resourceGroupName": { "type": "string", "metadata": { - "description": "The resource group of the deployed MX record." + "description": "The resource group of the deployed vulnerability assessment." }, "value": "[resourceGroup().name]" } @@ -79657,40 +91458,35 @@ } }, "dependsOn": [ - "privateDnsZone" + "server", + "server_securityAlertPolicies" ] }, - "privateDnsZone_PTR": { + "server_keys": { "copy": { - "name": "privateDnsZone_PTR", - "count": "[length(coalesce(parameters('ptr'), createArray()))]" + "name": "server_keys", + "count": "[length(parameters('keys'))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-PTRRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "name": "[format('{0}-Sql-Key-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "privateDnsZoneName": { + "serverName": { "value": "[parameters('name')]" }, "name": { - "value": "[coalesce(parameters('ptr'), createArray())[copyIndex()].name]" - }, - "metadata": { - "value": "[tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'metadata')]" - }, - "ptrRecords": { - "value": "[tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'ptrRecords')]" + "value": "[tryGet(parameters('keys')[copyIndex()], 'name')]" }, - "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'ttl'), 3600)]" + "serverKeyType": { + "value": "[tryGet(parameters('keys')[copyIndex()], 'serverKeyType')]" }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'roleAssignments')]" + "uri": { + "value": "[tryGet(parameters('keys')[copyIndex()], 'uri')]" } }, "template": { @@ -79700,206 +91496,85 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "3080404733048745471" - }, - "name": "Private DNS Zone PTR record", - "description": "This module deploys a Private DNS Zone PTR record.", - "owner": "Azure/module-maintainers" - }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } - } - } - }, - "nullable": true - } - }, - "parameters": { - "privateDnsZoneName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the PTR record." - } + "version": "0.34.44.8038", + "templateHash": "9797257758598562244" }, - "metadata": { - "type": "object", + "name": "Azure SQL Server Keys", + "description": "This module deploys an Azure SQL Server Key." + }, + "parameters": { + "name": { + "type": "string", "nullable": true, "metadata": { - "description": "Optional. The metadata attached to the record set." + "description": "Optional. The name of the key. Must follow the [__] pattern." } }, - "ptrRecords": { - "type": "array", - "nullable": true, + "serverName": { + "type": "string", "metadata": { - "description": "Optional. The list of PTR records in the record set." + "description": "Conditional. The name of the parent SQL server. Required if the template is used in a standalone deployment." } }, - "ttl": { - "type": "int", - "defaultValue": 3600, + "serverKeyType": { + "type": "string", + "defaultValue": "ServiceManaged", + "allowedValues": [ + "AzureKeyVault", + "ServiceManaged" + ], "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." + "description": "Optional. The server key type." } }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", + "uri": { + "type": "string", + "defaultValue": "", "metadata": { - "description": "Optional. Array of role assignments to create." + "description": "Optional. The URI of the server key. If the ServerKeyType is AzureKeyVault, then the URI is required. The AKV URI is required to be in this format: 'https://YourVaultName.azure.net/keys/YourKeyName/YourKeyVersion'." } } }, "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')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", - "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" - } + "splittedKeyUri": "[split(parameters('uri'), '/')]", + "serverKeyName": "[if(empty(parameters('uri')), 'ServiceManaged', format('{0}_{1}_{2}', split(variables('splittedKeyUri')[2], '.')[0], variables('splittedKeyUri')[4], variables('splittedKeyUri')[5]))]" }, "resources": { - "privateDnsZone": { + "server": { "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" + "type": "Microsoft.Sql/servers", + "apiVersion": "2023-08-01-preview", + "name": "[parameters('serverName')]" }, - "PTR": { - "type": "Microsoft.Network/privateDnsZones/PTR", - "apiVersion": "2020-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "key": { + "type": "Microsoft.Sql/servers/keys", + "apiVersion": "2023-08-01-preview", + "name": "[format('{0}/{1}', parameters('serverName'), coalesce(parameters('name'), variables('serverKeyName')))]", "properties": { - "metadata": "[parameters('metadata')]", - "ptrRecords": "[parameters('ptrRecords')]", - "ttl": "[parameters('ttl')]" + "serverKeyType": "[parameters('serverKeyType')]", + "uri": "[parameters('uri')]" } - }, - "PTR_roleAssignments": { - "copy": { - "name": "PTR_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/PTR/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/PTR', parameters('privateDnsZoneName'), 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": [ - "PTR" - ] } }, "outputs": { "name": { "type": "string", "metadata": { - "description": "The name of the deployed PTR record." + "description": "The name of the deployed server key." }, - "value": "[parameters('name')]" + "value": "[coalesce(parameters('name'), variables('serverKeyName'))]" }, "resourceId": { "type": "string", "metadata": { - "description": "The resource ID of the deployed PTR record." + "description": "The resource ID of the deployed server key." }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/PTR', parameters('privateDnsZoneName'), parameters('name'))]" + "value": "[resourceId('Microsoft.Sql/servers/keys', parameters('serverName'), coalesce(parameters('name'), variables('serverKeyName')))]" }, "resourceGroupName": { "type": "string", "metadata": { - "description": "The resource group of the deployed PTR record." + "description": "The resource group of the deployed server key." }, "value": "[resourceGroup().name]" } @@ -79907,41 +91582,30 @@ } }, "dependsOn": [ - "privateDnsZone" + "server" ] }, - "privateDnsZone_SOA": { - "copy": { - "name": "privateDnsZone_SOA", - "count": "[length(coalesce(parameters('soa'), createArray()))]" - }, + "cmk_key": { + "condition": "[not(equals(parameters('customerManagedKey'), null()))]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-SOARecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "name": "[format('{0}-Sql-Key', uniqueString(deployment().name, parameters('location')))]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "privateDnsZoneName": { + "serverName": { "value": "[parameters('name')]" }, "name": { - "value": "[coalesce(parameters('soa'), createArray())[copyIndex()].name]" - }, - "metadata": { - "value": "[tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'metadata')]" - }, - "soaRecord": { - "value": "[tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'soaRecord')]" + "value": "[format('{0}_{1}_{2}', last(split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')), tryGet(parameters('customerManagedKey'), 'keyName'), tryGet(parameters('customerManagedKey'), 'keyVersion'))]" }, - "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'ttl'), 3600)]" + "serverKeyType": { + "value": "AzureKeyVault" }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'roleAssignments')]" - } + "uri": "[if(not(empty(tryGet(parameters('customerManagedKey'), 'keyVersion'))), createObject('value', format('{0}/{1}', reference('cMKKeyVault::cMKKey').keyUri, tryGet(parameters('customerManagedKey'), 'keyVersion'))), if(coalesce(tryGet(parameters('customerManagedKey'), 'autoRotationEnabled'), true()), createObject('value', reference('cMKKeyVault::cMKKey').keyUri), createObject('value', reference('cMKKeyVault::cMKKey').keyUriWithVersion)))]" }, "template": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", @@ -79950,206 +91614,85 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "6653951445614700931" + "version": "0.34.44.8038", + "templateHash": "9797257758598562244" }, - "name": "Private DNS Zone SOA record", - "description": "This module deploys a Private DNS Zone SOA record.", - "owner": "Azure/module-maintainers" - }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } - } - } - }, - "nullable": true - } + "name": "Azure SQL Server Keys", + "description": "This module deploys an Azure SQL Server Key." }, "parameters": { - "privateDnsZoneName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." - } - }, "name": { "type": "string", - "metadata": { - "description": "Required. The name of the SOA record." - } - }, - "metadata": { - "type": "object", "nullable": true, "metadata": { - "description": "Optional. The metadata attached to the record set." + "description": "Optional. The name of the key. Must follow the [__] pattern." } }, - "soaRecord": { - "type": "object", - "nullable": true, + "serverName": { + "type": "string", "metadata": { - "description": "Optional. A SOA record." + "description": "Conditional. The name of the parent SQL server. Required if the template is used in a standalone deployment." } }, - "ttl": { - "type": "int", - "defaultValue": 3600, + "serverKeyType": { + "type": "string", + "defaultValue": "ServiceManaged", + "allowedValues": [ + "AzureKeyVault", + "ServiceManaged" + ], "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." + "description": "Optional. The server key type." } }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", + "uri": { + "type": "string", + "defaultValue": "", "metadata": { - "description": "Optional. Array of role assignments to create." + "description": "Optional. The URI of the server key. If the ServerKeyType is AzureKeyVault, then the URI is required. The AKV URI is required to be in this format: 'https://YourVaultName.azure.net/keys/YourKeyName/YourKeyVersion'." } } }, "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')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", - "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" - } + "splittedKeyUri": "[split(parameters('uri'), '/')]", + "serverKeyName": "[if(empty(parameters('uri')), 'ServiceManaged', format('{0}_{1}_{2}', split(variables('splittedKeyUri')[2], '.')[0], variables('splittedKeyUri')[4], variables('splittedKeyUri')[5]))]" }, "resources": { - "privateDnsZone": { + "server": { "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" + "type": "Microsoft.Sql/servers", + "apiVersion": "2023-08-01-preview", + "name": "[parameters('serverName')]" }, - "SOA": { - "type": "Microsoft.Network/privateDnsZones/SOA", - "apiVersion": "2020-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "key": { + "type": "Microsoft.Sql/servers/keys", + "apiVersion": "2023-08-01-preview", + "name": "[format('{0}/{1}', parameters('serverName'), coalesce(parameters('name'), variables('serverKeyName')))]", "properties": { - "metadata": "[parameters('metadata')]", - "soaRecord": "[parameters('soaRecord')]", - "ttl": "[parameters('ttl')]" + "serverKeyType": "[parameters('serverKeyType')]", + "uri": "[parameters('uri')]" } - }, - "SOA_roleAssignments": { - "copy": { - "name": "SOA_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/SOA/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/SOA', parameters('privateDnsZoneName'), 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": [ - "SOA" - ] } }, "outputs": { "name": { "type": "string", "metadata": { - "description": "The name of the deployed SOA record." + "description": "The name of the deployed server key." }, - "value": "[parameters('name')]" + "value": "[coalesce(parameters('name'), variables('serverKeyName'))]" }, "resourceId": { "type": "string", "metadata": { - "description": "The resource ID of the deployed SOA record." + "description": "The resource ID of the deployed server key." }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/SOA', parameters('privateDnsZoneName'), parameters('name'))]" + "value": "[resourceId('Microsoft.Sql/servers/keys', parameters('serverName'), coalesce(parameters('name'), variables('serverKeyName')))]" }, "resourceGroupName": { "type": "string", "metadata": { - "description": "The resource group of the deployed SOA record." + "description": "The resource group of the deployed server key." }, "value": "[resourceGroup().name]" } @@ -80157,227 +91700,342 @@ } }, "dependsOn": [ - "privateDnsZone" + "cMKKeyVault::cMKKey", + "server" ] }, - "privateDnsZone_SRV": { - "copy": { - "name": "privateDnsZone_SRV", - "count": "[length(coalesce(parameters('srv'), createArray()))]" - }, + "server_encryptionProtector": { + "condition": "[not(equals(parameters('customerManagedKey'), null()))]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-SRVRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "name": "[format('{0}-Sql-EncryProtector', uniqueString(deployment().name, parameters('location')))]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "privateDnsZoneName": { + "sqlServerName": { "value": "[parameters('name')]" }, - "name": { - "value": "[coalesce(parameters('srv'), createArray())[copyIndex()].name]" - }, - "metadata": { - "value": "[tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'metadata')]" - }, - "srvRecords": { - "value": "[tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'srvRecords')]" + "serverKeyName": { + "value": "[reference('cmk_key').outputs.name.value]" }, - "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'ttl'), 3600)]" + "serverKeyType": { + "value": "AzureKeyVault" }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'roleAssignments')]" + "autoRotationEnabled": { + "value": "[tryGet(parameters('customerManagedKey'), 'autoRotationEnabled')]" } }, "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.32.4.45862", - "templateHash": "5790774778713328446" + "version": "0.34.44.8038", + "templateHash": "13156357596009101804" }, - "name": "Private DNS Zone SRV record", - "description": "This module deploys a Private DNS Zone SRV record.", - "owner": "Azure/module-maintainers" + "name": "Azure SQL Server Encryption Protector", + "description": "This module deploys an Azure SQL Server Encryption Protector." }, - "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } - } - } + "parameters": { + "sqlServerName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the sql server. Required if the template is used in a standalone deployment." + } + }, + "serverKeyName": { + "type": "string", + "metadata": { + "description": "Required. The name of the server key." + } + }, + "autoRotationEnabled": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Key auto rotation opt-in flag." + } + }, + "serverKeyType": { + "type": "string", + "defaultValue": "ServiceManaged", + "allowedValues": [ + "AzureKeyVault", + "ServiceManaged" + ], + "metadata": { + "description": "Optional. The encryption protector type." + } + } + }, + "resources": [ + { + "type": "Microsoft.Sql/servers/encryptionProtector", + "apiVersion": "2023-08-01-preview", + "name": "[format('{0}/{1}', parameters('sqlServerName'), 'current')]", + "properties": { + "serverKeyType": "[parameters('serverKeyType')]", + "autoRotationEnabled": "[parameters('autoRotationEnabled')]", + "serverKeyName": "[parameters('serverKeyName')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed encryption protector." }, - "nullable": true + "value": "current" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the encryption protector." + }, + "value": "[resourceId('Microsoft.Sql/servers/encryptionProtector', parameters('sqlServerName'), 'current')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed encryption protector." + }, + "value": "[resourceGroup().name]" } + } + } + }, + "dependsOn": [ + "cmk_key", + "server" + ] + }, + "server_audit_settings": { + "condition": "[not(empty(parameters('auditSettings')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Sql-AuditSettings', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "serverName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(tryGet(parameters('auditSettings'), 'name'), 'default')]" + }, + "state": { + "value": "[tryGet(parameters('auditSettings'), 'state')]" + }, + "auditActionsAndGroups": { + "value": "[tryGet(parameters('auditSettings'), 'auditActionsAndGroups')]" + }, + "isAzureMonitorTargetEnabled": { + "value": "[tryGet(parameters('auditSettings'), 'isAzureMonitorTargetEnabled')]" + }, + "isDevopsAuditEnabled": { + "value": "[tryGet(parameters('auditSettings'), 'isDevopsAuditEnabled')]" + }, + "isManagedIdentityInUse": { + "value": "[tryGet(parameters('auditSettings'), 'isManagedIdentityInUse')]" + }, + "isStorageSecondaryKeyInUse": { + "value": "[tryGet(parameters('auditSettings'), 'isStorageSecondaryKeyInUse')]" + }, + "queueDelayMs": { + "value": "[tryGet(parameters('auditSettings'), 'queueDelayMs')]" + }, + "retentionDays": { + "value": "[tryGet(parameters('auditSettings'), 'retentionDays')]" + }, + "storageAccountResourceId": { + "value": "[tryGet(parameters('auditSettings'), 'storageAccountResourceId')]" + } + }, + "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.34.44.8038", + "templateHash": "15469959027924260105" + }, + "name": "Azure SQL Server Audit Settings", + "description": "This module deploys an Azure SQL Server Audit Settings." }, "parameters": { - "privateDnsZoneName": { + "name": { "type": "string", "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + "description": "Required. The name of the audit settings." } }, - "name": { + "serverName": { "type": "string", "metadata": { - "description": "Required. The name of the SRV record." + "description": "Conditional. The Name of SQL Server. Required if the template is used in a standalone deployment." } }, - "metadata": { - "type": "object", - "nullable": true, + "state": { + "type": "string", + "defaultValue": "Enabled", + "allowedValues": [ + "Enabled", + "Disabled" + ], "metadata": { - "description": "Optional. The metadata attached to the record set." + "description": "Optional. Specifies the state of the audit. If state is Enabled, storageEndpoint or isAzureMonitorTargetEnabled are required." } }, - "srvRecords": { + "auditActionsAndGroups": { "type": "array", - "nullable": true, + "items": { + "type": "string" + }, + "defaultValue": [ + "BATCH_COMPLETED_GROUP", + "SUCCESSFUL_DATABASE_AUTHENTICATION_GROUP", + "FAILED_DATABASE_AUTHENTICATION_GROUP" + ], "metadata": { - "description": "Optional. The list of SRV records in the record set." + "description": "Optional. Specifies the Actions-Groups and Actions to audit." } }, - "ttl": { + "isAzureMonitorTargetEnabled": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Specifies whether audit events are sent to Azure Monitor." + } + }, + "isDevopsAuditEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Specifies the state of devops audit. If state is Enabled, devops logs will be sent to Azure Monitor." + } + }, + "isManagedIdentityInUse": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Specifies whether Managed Identity is used to access blob storage." + } + }, + "isStorageSecondaryKeyInUse": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Specifies whether storageAccountAccessKey value is the storage's secondary key." + } + }, + "queueDelayMs": { "type": "int", - "defaultValue": 3600, + "defaultValue": 1000, "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." + "description": "Optional. Specifies the amount of time in milliseconds that can elapse before audit actions are forced to be processed." } }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", + "retentionDays": { + "type": "int", + "defaultValue": 90, "metadata": { - "description": "Optional. Array of role assignments to create." + "description": "Optional. Specifies the number of days to keep in the audit logs in the storage account." } - } - }, - "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)))))]" + }, + "storageAccountResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. A blob storage to hold the auditing storage account." } - ], - "builtInRoleNames": { - "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", - "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, "resources": { - "privateDnsZone": { + "server": { "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" + "type": "Microsoft.Sql/servers", + "apiVersion": "2023-08-01-preview", + "name": "[parameters('serverName')]" }, - "SRV": { - "type": "Microsoft.Network/privateDnsZones/SRV", - "apiVersion": "2020-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "auditSettings": { + "type": "Microsoft.Sql/servers/auditingSettings", + "apiVersion": "2023-08-01-preview", + "name": "[format('{0}/{1}', parameters('serverName'), parameters('name'))]", "properties": { - "metadata": "[parameters('metadata')]", - "srvRecords": "[parameters('srvRecords')]", - "ttl": "[parameters('ttl')]" + "state": "[parameters('state')]", + "auditActionsAndGroups": "[parameters('auditActionsAndGroups')]", + "isAzureMonitorTargetEnabled": "[parameters('isAzureMonitorTargetEnabled')]", + "isDevopsAuditEnabled": "[parameters('isDevopsAuditEnabled')]", + "isManagedIdentityInUse": "[parameters('isManagedIdentityInUse')]", + "isStorageSecondaryKeyInUse": "[parameters('isStorageSecondaryKeyInUse')]", + "queueDelayMs": "[parameters('queueDelayMs')]", + "retentionDays": "[parameters('retentionDays')]", + "storageAccountAccessKey": "[if(and(not(empty(parameters('storageAccountResourceId'))), not(parameters('isManagedIdentityInUse'))), listKeys(parameters('storageAccountResourceId'), '2019-06-01').keys[0].value, null())]", + "storageAccountSubscriptionId": "[if(not(empty(parameters('storageAccountResourceId'))), split(parameters('storageAccountResourceId'), '/')[2], null())]", + "storageEndpoint": "[if(not(empty(parameters('storageAccountResourceId'))), format('https://{0}.blob.{1}', last(split(parameters('storageAccountResourceId'), '/')), environment().suffixes.storage), null())]" } }, - "SRV_roleAssignments": { - "copy": { - "name": "SRV_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/SRV/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/SRV', parameters('privateDnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "storageAccount_sbdc_rbac": { + "condition": "[and(parameters('isManagedIdentityInUse'), not(empty(parameters('storageAccountResourceId'))))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-stau-rbac', parameters('serverName'))]", "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')]" + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "storageAccountName": { + "value": "[last(split(parameters('storageAccountResourceId'), '/'))]" + }, + "managedIdentityPrincipalId": "[if(equals(reference('server', '2023-08-01-preview', 'full').identity.type, 'UserAssigned'), createObject('value', filter(items(reference('server', '2023-08-01-preview', 'full').identity.userAssignedIdentities), lambda('identity', equals(lambdaVariables('identity').key, reference('server').primaryUserAssignedIdentityId)))[0].value.principalId), createObject('value', reference('server', '2023-08-01-preview', 'full').identity.principalId))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.34.44.8038", + "templateHash": "11839727322069180104" + } + }, + "parameters": { + "storageAccountName": { + "type": "string" + }, + "managedIdentityPrincipalId": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}', parameters('storageAccountName'))]", + "name": "[guid(format('{0}-{1}-Storage-Blob-Data-Contributor', resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName')), parameters('managedIdentityPrincipalId')))]", + "properties": { + "roleDefinitionId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe')]", + "principalId": "[parameters('managedIdentityPrincipalId')]", + "principalType": "ServicePrincipal" + } + } + ] + } }, "dependsOn": [ - "SRV" + "server" ] } }, @@ -80385,21 +92043,21 @@ "name": { "type": "string", "metadata": { - "description": "The name of the deployed SRV record." + "description": "The name of the deployed audit settings." }, "value": "[parameters('name')]" }, "resourceId": { "type": "string", "metadata": { - "description": "The resource ID of the deployed SRV record." + "description": "The resource ID of the deployed audit settings." }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/SRV', parameters('privateDnsZoneName'), parameters('name'))]" + "value": "[resourceId('Microsoft.Sql/servers/auditingSettings', parameters('serverName'), parameters('name'))]" }, "resourceGroupName": { "type": "string", "metadata": { - "description": "The resource group of the deployed SRV record." + "description": "The resource group of the deployed audit settings." }, "value": "[resourceGroup().name]" } @@ -80407,40 +92065,27 @@ } }, "dependsOn": [ - "privateDnsZone" + "server" ] }, - "privateDnsZone_TXT": { - "copy": { - "name": "privateDnsZone_TXT", - "count": "[length(coalesce(parameters('txt'), createArray()))]" - }, + "secretsExport": { + "condition": "[not(equals(parameters('secretsExportConfiguration'), null()))]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-TXTRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "name": "[format('{0}-secrets-kv', uniqueString(deployment().name, parameters('location')))]", + "subscriptionId": "[split(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '/')[2]]", + "resourceGroup": "[split(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '/')[4]]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "privateDnsZoneName": { - "value": "[parameters('name')]" - }, - "name": { - "value": "[coalesce(parameters('txt'), createArray())[copyIndex()].name]" - }, - "metadata": { - "value": "[tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'metadata')]" - }, - "txtRecords": { - "value": "[tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'txtRecords')]" - }, - "ttl": { - "value": "[coalesce(tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'ttl'), 3600)]" + "keyVaultName": { + "value": "[last(split(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '/'))]" }, - "roleAssignments": { - "value": "[tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'roleAssignments')]" + "secretsToSet": { + "value": "[union(createArray(), if(contains(parameters('secretsExportConfiguration'), 'sqlAdminPasswordSecretName'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'sqlAdminPasswordSecretName'), 'value', parameters('administratorLoginPassword'))), createArray()), if(contains(parameters('secretsExportConfiguration'), 'sqlAzureConnectionStringSercretName'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'sqlAzureConnectionStringSercretName'), 'value', format('Server={0}; Database={1}; User={2}; Password={3}', reference('server').fullyQualifiedDomainName, if(not(empty(parameters('databases'))), parameters('databases')[0].name, ''), parameters('administratorLogin'), parameters('administratorLoginPassword')))), createArray()))]" } }, "template": { @@ -80450,250 +92095,163 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "1855369119498044639" - }, - "name": "Private DNS Zone TXT record", - "description": "This module deploys a Private DNS Zone TXT record.", - "owner": "Azure/module-maintainers" + "version": "0.34.44.8038", + "templateHash": "12919538841236982879" + } }, "definitions": { - "roleAssignmentType": { - "type": "array", - "items": { - "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." - } + "secretSetOutputType": { + "type": "object", + "properties": { + "secretResourceId": { + "type": "string", + "metadata": { + "description": "The resourceId of the exported secret." + } + }, + "secretUri": { + "type": "string", + "metadata": { + "description": "The secret URI of the exported secret." + } + }, + "secretUriWithVersion": { + "type": "string", + "metadata": { + "description": "The secret URI with version of the exported secret." } } }, - "nullable": true - } - }, - "parameters": { - "privateDnsZoneName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." - } - }, - "name": { - "type": "string", "metadata": { - "description": "Required. The name of the TXT record." + "description": "An AVM-aligned type for the output of the secret set via the secrets export feature.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } } }, - "metadata": { + "secretToSetType": { "type": "object", - "nullable": true, + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the secret to set." + } + }, + "value": { + "type": "securestring", + "metadata": { + "description": "Required. The value of the secret to set." + } + } + }, "metadata": { - "description": "Optional. The metadata attached to the record set." + "description": "An AVM-aligned type for the secret to set via the secrets export feature.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } } - }, - "ttl": { - "type": "int", - "defaultValue": 3600, + } + }, + "parameters": { + "keyVaultName": { + "type": "string", "metadata": { - "description": "Optional. The TTL (time-to-live) of the records in the record set." + "description": "Required. The name of the Key Vault to set the secrets in." } }, - "txtRecords": { + "secretsToSet": { "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. The list of TXT records in the record set." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", + "items": { + "$ref": "#/definitions/secretToSetType" + }, "metadata": { - "description": "Optional. Array of role assignments to create." - } - } - }, - "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)))))]" + "description": "Required. The secrets to set in the Key Vault." } - ], - "builtInRoleNames": { - "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", - "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", - "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, "resources": { - "privateDnsZone": { + "keyVault": { "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" - }, - "TXT": { - "type": "Microsoft.Network/privateDnsZones/TXT", - "apiVersion": "2020-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "properties": { - "metadata": "[parameters('metadata')]", - "ttl": "[parameters('ttl')]", - "txtRecords": "[parameters('txtRecords')]" - } + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2022-07-01", + "name": "[parameters('keyVaultName')]" }, - "TXT_roleAssignments": { + "secrets": { "copy": { - "name": "TXT_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/TXT/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/TXT', parameters('privateDnsZoneName'), 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')]" + "name": "secrets", + "count": "[length(parameters('secretsToSet'))]" }, - "dependsOn": [ - "TXT" - ] + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2023-07-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('secretsToSet')[copyIndex()].name)]", + "properties": { + "value": "[parameters('secretsToSet')[copyIndex()].value]" + } } }, "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the deployed TXT record." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the deployed TXT record." + "secretsSet": { + "type": "array", + "items": { + "$ref": "#/definitions/secretSetOutputType" }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/TXT', parameters('privateDnsZoneName'), parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", "metadata": { - "description": "The resource group of the deployed TXT record." + "description": "The references to the secrets exported to the provided Key Vault." }, - "value": "[resourceGroup().name]" + "copy": { + "count": "[length(range(0, length(coalesce(parameters('secretsToSet'), createArray()))))]", + "input": { + "secretResourceId": "[resourceId('Microsoft.KeyVault/vaults/secrets', parameters('keyVaultName'), parameters('secretsToSet')[range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()]].name)]", + "secretUri": "[reference(format('secrets[{0}]', range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()])).secretUri]", + "secretUriWithVersion": "[reference(format('secrets[{0}]', range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()])).secretUriWithVersion]" + } + } } } } }, "dependsOn": [ - "privateDnsZone" + "server" ] }, - "privateDnsZone_virtualNetworkLinks": { + "failover_groups": { "copy": { - "name": "privateDnsZone_virtualNetworkLinks", - "count": "[length(coalesce(parameters('virtualNetworkLinks'), createArray()))]" + "name": "failover_groups", + "count": "[length(parameters('failoverGroups'))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-PrivateDnsZone-VirtualNetworkLink-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "name": "[format('{0}-Sql-FailoverGroup-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "privateDnsZoneName": { - "value": "[parameters('name')]" - }, "name": { - "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'name'), format('{0}-vnetlink', last(split(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()].virtualNetworkResourceId, '/'))))]" + "value": "[parameters('failoverGroups')[copyIndex()].name]" }, - "virtualNetworkResourceId": { - "value": "[coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()].virtualNetworkResourceId]" + "tags": { + "value": "[coalesce(tryGet(parameters('failoverGroups')[copyIndex()], 'tags'), parameters('tags'))]" }, - "location": { - "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'location'), 'global')]" + "serverName": { + "value": "[parameters('name')]" }, - "registrationEnabled": { - "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'registrationEnabled'), false())]" + "databases": { + "value": "[parameters('failoverGroups')[copyIndex()].databases]" }, - "tags": { - "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + "partnerServers": { + "value": "[parameters('failoverGroups')[copyIndex()].partnerServers]" }, - "resolutionPolicy": { - "value": "[tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'resolutionPolicy')]" + "readOnlyEndpoint": { + "value": "[tryGet(parameters('failoverGroups')[copyIndex()], 'readOnlyEndpoint')]" + }, + "readWriteEndpoint": { + "value": "[parameters('failoverGroups')[copyIndex()].readWriteEndpoint]" + }, + "secondaryType": { + "value": "[parameters('failoverGroups')[copyIndex()].secondaryType]" } }, "template": { @@ -80703,81 +92261,155 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.32.4.45862", - "templateHash": "15326596012552051215" + "version": "0.34.44.8038", + "templateHash": "5841212065211909434" }, - "name": "Private DNS Zone Virtual Network Link", - "description": "This module deploys a Private DNS Zone Virtual Network Link.", - "owner": "Azure/module-maintainers" + "name": "Azure SQL Server failover group", + "description": "This module deploys Azure SQL Server failover group." }, - "parameters": { - "privateDnsZoneName": { - "type": "string", + "definitions": { + "failoverGroupReadOnlyEndpointType": { + "type": "object", + "properties": { + "failoverPolicy": { + "type": "string", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "metadata": { + "description": "Required. Failover policy of the read-only endpoint for the failover group." + } + }, + "targetServer": { + "type": "string", + "metadata": { + "description": "Required. The target partner server where the read-only endpoint points to." + } + } + }, "metadata": { - "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + "__bicep_export!": true } }, + "failoverGroupReadWriteEndpointType": { + "type": "object", + "properties": { + "failoverPolicy": { + "type": "string", + "allowedValues": [ + "Automatic", + "Manual" + ], + "metadata": { + "description": "Required. Failover policy of the read-write endpoint for the failover group. If failoverPolicy is Automatic then failoverWithDataLossGracePeriodMinutes is required." + } + }, + "failoverWithDataLossGracePeriodMinutes": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Grace period before failover with data loss is attempted for the read-write endpoint." + } + } + }, + "metadata": { + "__bicep_export!": true + } + } + }, + "parameters": { "name": { "type": "string", - "defaultValue": "[format('{0}-vnetlink', last(split(parameters('virtualNetworkResourceId'), '/')))]", "metadata": { - "description": "Optional. The name of the virtual network link." + "description": "Required. The name of the failover group." } }, - "location": { + "serverName": { "type": "string", - "defaultValue": "global", "metadata": { - "description": "Optional. The location of the PrivateDNSZone. Should be global." + "description": "Conditional. The Name of SQL Server. Required if the template is used in a standalone deployment." } }, - "tags": { - "type": "object", + "databases": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. List of databases in the failover group." + } + }, + "partnerServers": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. List of the partner servers for the failover group." + } + }, + "readOnlyEndpoint": { + "$ref": "#/definitions/failoverGroupReadOnlyEndpointType", "nullable": true, "metadata": { - "description": "Optional. Tags of the resource." + "description": "Optional. Read-only endpoint of the failover group instance." } }, - "registrationEnabled": { - "type": "bool", - "defaultValue": false, + "readWriteEndpoint": { + "$ref": "#/definitions/failoverGroupReadWriteEndpointType", "metadata": { - "description": "Optional. Is auto-registration of virtual machine records in the virtual network in the Private DNS zone enabled?." + "description": "Required. Read-write endpoint of the failover group instance." } }, - "virtualNetworkResourceId": { + "secondaryType": { "type": "string", + "allowedValues": [ + "Geo", + "Standby" + ], "metadata": { - "description": "Required. Link to another virtual network resource ID." + "description": "Required. Databases secondary type on partner server." } }, - "resolutionPolicy": { - "type": "string", + "tags": { + "type": "object", "nullable": true, "metadata": { - "description": "Optional. The resolution policy on the virtual network link. Only applicable for virtual network links to privatelink zones, and for A,AAAA,CNAME queries. When set to `NxDomainRedirect`, Azure DNS resolver falls back to public resolution if private dns query resolution results in non-existent domain response. `Default` is configured as the default option." + "description": "Optional. Tags of the resource." } } }, "resources": { - "privateDnsZone": { + "server": { "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[parameters('privateDnsZoneName')]" + "type": "Microsoft.Sql/servers", + "apiVersion": "2023-08-01-preview", + "name": "[parameters('serverName')]" }, - "virtualNetworkLink": { - "type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks", - "apiVersion": "2024-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", - "location": "[parameters('location')]", + "failoverGroup": { + "type": "Microsoft.Sql/servers/failoverGroups", + "apiVersion": "2024-05-01-preview", + "name": "[format('{0}/{1}', parameters('serverName'), parameters('name'))]", "tags": "[parameters('tags')]", "properties": { - "registrationEnabled": "[parameters('registrationEnabled')]", - "virtualNetwork": { - "id": "[parameters('virtualNetworkResourceId')]" - }, - "resolutionPolicy": "[parameters('resolutionPolicy')]" + "copy": [ + { + "name": "databases", + "count": "[length(parameters('databases'))]", + "input": "[resourceId('Microsoft.Sql/servers/databases', parameters('serverName'), parameters('databases')[copyIndex('databases')])]" + }, + { + "name": "partnerServers", + "count": "[length(parameters('partnerServers'))]", + "input": { + "id": "[resourceId(resourceGroup().name, 'Microsoft.Sql/servers', parameters('partnerServers')[copyIndex('partnerServers')])]" + } + } + ], + "readOnlyEndpoint": "[if(not(empty(parameters('readOnlyEndpoint'))), createObject('failoverPolicy', parameters('readOnlyEndpoint').failoverPolicy, 'targetServer', resourceId(resourceGroup().name, 'Microsoft.Sql/servers', parameters('readOnlyEndpoint').targetServer)), null())]", + "readWriteEndpoint": "[parameters('readWriteEndpoint')]", + "secondaryType": "[parameters('secondaryType')]" } } }, @@ -80785,76 +92417,470 @@ "name": { "type": "string", "metadata": { - "description": "The name of the deployed virtual network link." + "description": "The name of the deployed failover group." }, "value": "[parameters('name')]" }, "resourceId": { "type": "string", "metadata": { - "description": "The resource ID of the deployed virtual network link." + "description": "The resource ID of the deployed failover group." }, - "value": "[resourceId('Microsoft.Network/privateDnsZones/virtualNetworkLinks', parameters('privateDnsZoneName'), parameters('name'))]" + "value": "[resourceId('Microsoft.Sql/servers/failoverGroups', parameters('serverName'), parameters('name'))]" }, "resourceGroupName": { "type": "string", "metadata": { - "description": "The resource group of the deployed virtual network link." + "description": "The resource group of the deployed failover group." }, "value": "[resourceGroup().name]" - }, - "location": { - "type": "string", - "metadata": { - "description": "The location the resource was deployed into." - }, - "value": "[reference('virtualNetworkLink', '2024-06-01', 'full').location]" } } } }, "dependsOn": [ - "privateDnsZone" + "server", + "server_databases" ] } }, "outputs": { - "resourceGroupName": { + "name": { "type": "string", "metadata": { - "description": "The resource group the private DNS zone was deployed into." + "description": "The name of the deployed SQL server." }, - "value": "[resourceGroup().name]" + "value": "[parameters('name')]" }, - "name": { + "resourceId": { "type": "string", "metadata": { - "description": "The name of the private DNS zone." + "description": "The resource ID of the deployed SQL server." }, - "value": "[parameters('name')]" + "value": "[resourceId('Microsoft.Sql/servers', parameters('name'))]" }, - "resourceId": { + "fullyQualifiedDomainName": { "type": "string", "metadata": { - "description": "The resource ID of the private DNS zone." + "description": "The fully qualified domain name of the deployed SQL server." }, - "value": "[resourceId('Microsoft.Network/privateDnsZones', parameters('name'))]" + "value": "[reference('server').fullyQualifiedDomainName]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed SQL server." + }, + "value": "[resourceGroup().name]" + }, + "systemAssignedMIPrincipalId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "The principal ID of the system assigned identity." + }, + "value": "[tryGet(tryGet(reference('server', '2023-08-01-preview', 'full'), 'identity'), 'principalId')]" }, "location": { "type": "string", "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('privateDnsZone', '2020-06-01', 'full').location]" + "value": "[reference('server', '2023-08-01-preview', 'full').location]" + }, + "exportedSecrets": { + "$ref": "#/definitions/secretsOutputType", + "metadata": { + "description": "A hashtable of references to the secrets exported to the provided Key Vault. The key of each reference is each secret's name." + }, + "value": "[if(not(equals(parameters('secretsExportConfiguration'), null())), toObject(reference('secretsExport').outputs.secretsSet.value, lambda('secret', last(split(lambdaVariables('secret').secretResourceId, '/'))), lambda('secret', lambdaVariables('secret'))), createObject())]" + }, + "privateEndpoints": { + "type": "array", + "items": { + "$ref": "#/definitions/privateEndpointOutputType" + }, + "metadata": { + "description": "The private endpoints of the SQL server." + }, + "copy": { + "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]", + "input": { + "name": "[reference(format('server_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", + "resourceId": "[reference(format('server_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", + "groupId": "[tryGet(tryGet(reference(format('server_privateEndpoints[{0}]', copyIndex())).outputs, 'groupId'), 'value')]", + "customDnsConfigs": "[reference(format('server_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfigs.value]", + "networkInterfaceResourceIds": "[reference(format('server_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceResourceIds.value]" + } + } } } } + }, + "dependsOn": [ + "privateDnsZone" + ] + } + }, + "outputs": { + "resourceId": { + "type": "string", + "value": "[reference('sqlServer').outputs.resourceId.value]" + }, + "name": { + "type": "string", + "value": "[reference('sqlServer').outputs.name.value]" + } + } + } + }, + "dependsOn": [ + "network" + ] + }, + "appService": { + "condition": "[variables('deploySampleApp')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('{0}-app-service-deployment', parameters('name')), 64)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[format('app-{0}{1}', parameters('name'), variables('resourceToken'))]" + }, + "location": { + "value": "[parameters('location')]" + }, + "userAssignedIdentityName": { + "value": "[reference('appIdentity').outputs.name.value]" + }, + "appInsightsName": { + "value": "[reference('applicationInsights').outputs.name.value]" + }, + "keyVaultName": { + "value": "[reference('keyvault').outputs.name.value]" + }, + "logAnalyticsWorkspaceResourceId": { + "value": "[reference('logAnalyticsWorkspace').outputs.resourceId.value]" + }, + "skuName": { + "value": "B3" + }, + "skuCapacity": { + "value": 1 + }, + "imagePath": { + "value": "sampleappaoaichatgpt.azurecr.io/sample-app-aoai-chatgpt" + }, + "imageTag": { + "value": "2025-02-13_52" + }, + "virtualNetworkSubnetId": "[if(parameters('networkIsolation'), createObject('value', reference('network').outputs.appSubnetResourceId.value), createObject('value', ''))]", + "authProvider": { + "value": { + "clientId": "[coalesce(parameters('authClientId'), '')]", + "clientSecretName": "[variables('authClientSecretName')]", + "openIdIssuer": "[format('{0}{1}/v2.0', environment().authentication.loginEndpoint, tenant().tenantId)]" + } + }, + "searchServiceConfiguration": { + "value": { + "name": "[reference('aiSearch').outputs.name.value]", + "indexName": "ai_app_index" + } + }, + "cosmosDbConfiguration": { + "value": { + "account": "[reference('cosmosDb').outputs.cosmosDBname.value]", + "database": "[parameters('cosmosDatabases')[0].name]", + "container": "[coalesce(tryGet(tryGet(parameters('cosmosDatabases')[0], 'containers', 0), 'name'), '')]" + } + }, + "openAIConfiguration": { + "value": { + "name": "[reference('cognitiveServices').outputs.aiServicesName.value]", + "endpoint": "[reference('cognitiveServices').outputs.aiServicesEndpoint.value]", + "gptModelName": "[parameters('aiGPTModelDeployment').modelName]", + "gptModelDeploymentName": "[parameters('aiGPTModelDeployment').modelName]", + "embeddingModelDeploymentName": "[parameters('aiEmbeddingModelDeployment').modelName]" + } + } + }, + "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.1.42791", + "templateHash": "16909276659049541183" + } + }, + "definitions": { + "authIdentityProvider": { + "type": "object", + "properties": { + "clientId": { + "type": "string", + "metadata": { + "description": "Required. The client/application ID of the Entra application." + } + }, + "clientSecretName": { + "type": "string", + "metadata": { + "description": "Required. The secret name of the Azure Active Directory application secret stored in Key Vault." + } + }, + "openIdIssuer": { + "type": "string", + "metadata": { + "description": "Required. The OpenID issuer of the Entra application." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "Values for setting up authentication with Entra provider." + } + }, + "cosmosDbConfig": { + "type": "object", + "properties": { + "account": { + "type": "string", + "metadata": { + "description": "Required. The name of the Cosmos DB account." + } + }, + "database": { + "type": "string", + "metadata": { + "description": "Required. The name of the Cosmos DB database." + } + }, + "container": { + "type": "string", + "metadata": { + "description": "Required. The name of the Cosmos DB container." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "Values for setting up Cosmos DB configuration." + } + }, + "searchServiceConfig": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the Azure Search service." + } + }, + "indexName": { + "type": "string", + "metadata": { + "description": "Required. The name of the Azure Search index." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "Values for setting up Azure Search configuration." + } + }, + "openAIConfig": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the OpenAI resource." + } + }, + "endpoint": { + "type": "string", + "metadata": { + "description": "Required. The endpoint of the OpenAI resource." + } + }, + "embeddingModelDeploymentName": { + "type": "string", + "metadata": { + "description": "Required. The name of the OpenAI embedding model deployment." + } + }, + "gptModelName": { + "type": "string", + "metadata": { + "description": "Required. The name of the OpenAI GPT model (gpt-4o)." + } + }, + "gptModelDeploymentName": { + "type": "string", + "metadata": { + "description": "Required. The name of the OpenAI GPT model deployment." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "Values for setting up OpenAI configuration." + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Name of the App Service resource." + } + }, + "location": { + "type": "string", + "metadata": { + "description": "Specifies the location for all the Azure resources." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Tags to be applied to the resources." + } + }, + "skuName": { + "type": "string", + "allowedValues": [ + "B1", + "B2", + "B3", + "P0V3", + "P1V3", + "P2V3", + "P3V3", + "P1mv3", + "P2mv3", + "P3mv3", + "P4mv3", + "P5mv3" + ], + "metadata": { + "description": "The SKU name for the App Service Plan." + } + }, + "skuCapacity": { + "type": "int", + "allowedValues": [ + 1, + 2, + 3 + ], + "metadata": { + "description": "The SKU capacity for the App Service Plan." + } + }, + "virtualNetworkSubnetId": { + "type": "string", + "metadata": { + "description": "Resource ID of the virtual network subnet to integrate the App Service." + } + }, + "userAssignedIdentityName": { + "type": "string", + "metadata": { + "description": "Resource Name of the user-assigned managed identity to assign to the App Service." + } + }, + "logAnalyticsWorkspaceResourceId": { + "type": "string", + "metadata": { + "description": "Resource ID of the Log Analytics workspace to use for diagnostic settings." + } + }, + "appInsightsName": { + "type": "string", + "metadata": { + "description": "Name of an existing Application Insights resource for the App Service." + } + }, + "keyVaultName": { + "type": "string", + "metadata": { + "description": "Name of existing Key Vault to read secrets." + } + }, + "imagePath": { + "type": "string", + "metadata": { + "description": "Full path to container image." } }, - "sqlServer": { + "imageTag": { + "type": "string", + "metadata": { + "description": "Tag for the image version." + } + }, + "authProvider": { + "$ref": "#/definitions/authIdentityProvider", + "metadata": { + "description": "Auth configuration for the App Service when registering Entra Identity Provider" + } + }, + "cosmosDbConfiguration": { + "$ref": "#/definitions/cosmosDbConfig", + "metadata": { + "description": "Cosmos DB configuration for the App Service for storing conversation history." + } + }, + "searchServiceConfiguration": { + "$ref": "#/definitions/searchServiceConfig", + "metadata": { + "description": "Azure Search configuration for the App Service for searching vector content as part of the RAG chat pattern." + } + }, + "openAIConfiguration": { + "$ref": "#/definitions/openAIConfig", + "metadata": { + "description": "OpenAI configuration for the App Service for embedding and GPT model." + } + } + }, + "variables": { + "nameFormatted": "[take(toLower(parameters('name')), 55)]" + }, + "resources": { + "appInsights": { + "existing": true, + "type": "Microsoft.Insights/components", + "apiVersion": "2020-02-02", + "name": "[parameters('appInsightsName')]" + }, + "userAssignedIdentity": { + "existing": true, + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2024-11-30", + "name": "[parameters('userAssignedIdentityName')]" + }, + "keyVault": { + "existing": true, + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2024-11-01", + "name": "[parameters('keyVaultName')]" + }, + "appServicePlan": { "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[take(format('{0}-sqlserver-deployment', variables('nameFormatted')), 64)]", + "name": "[take(format('{0}-app-service-plan-deployment', variables('nameFormatted')), 64)]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -80862,34 +92888,39 @@ "mode": "Incremental", "parameters": { "name": { - "value": "[variables('nameFormatted')]" - }, - "administratorLogin": { - "value": "[parameters('administratorLogin')]" - }, - "administratorLoginPassword": { - "value": "[parameters('administratorLoginPassword')]" - }, - "databases": { - "value": "[parameters('databases')]" + "value": "[format('asp-{0}', variables('nameFormatted'))]" }, "location": { "value": "[parameters('location')]" }, - "managedIdentities": { - "value": { - "systemAssigned": true - } + "tags": { + "value": "[parameters('tags')]" }, - "restrictOutboundNetworkAccess": { - "value": "Disabled" + "kind": { + "value": "linux" }, - "roleAssignments": { - "value": "[parameters('roleAssignments')]" + "skuName": { + "value": "[parameters('skuName')]" }, - "privateEndpoints": "[if(parameters('networkIsolation'), createObject('value', createArray(createObject('privateDnsZoneGroup', createObject('privateDnsZoneGroupConfigs', createArray(createObject('privateDnsZoneResourceId', reference('privateDnsZone').outputs.resourceId.value))), 'service', 'sqlServer', 'subnetResourceId', parameters('virtualNetworkSubnetResourceId')))), createObject('value', createArray()))]", - "tags": { - "value": "[parameters('tags')]" + "skuCapacity": { + "value": "[parameters('skuCapacity')]" + }, + "reserved": { + "value": true + }, + "zoneRedundant": "[if(or(or(startsWith(parameters('skuName'), 'F'), startsWith(parameters('skuName'), 'B')), equals(parameters('skuCapacity'), 1)), createObject('value', false()), createObject('value', true()))]", + "diagnosticSettings": { + "value": [ + { + "metricCategories": [ + { + "category": "AllMetrics" + } + ], + "name": "customSetting", + "workspaceResourceId": "[parameters('logAnalyticsWorkspaceResourceId')]" + } + ] } }, "template": { @@ -80897,982 +92928,807 @@ "languageVersion": "2.0", "contentVersion": "1.0.0.0", "metadata": { - "_generator": { - "name": "bicep", - "version": "0.34.44.8038", - "templateHash": "5205596651564295650" - }, - "name": "Azure SQL Servers", - "description": "This module deploys an Azure SQL Server." - }, - "definitions": { - "privateEndpointOutputType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the private endpoint." - } - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the private endpoint." - } - }, - "groupId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "The group Id for the private endpoint Group." - } - }, - "customDnsConfigs": { - "type": "array", - "items": { - "type": "object", - "properties": { - "fqdn": { - "type": "string", - "nullable": true, - "metadata": { - "description": "FQDN that resolves to private endpoint IP address." - } - }, - "ipAddresses": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "A list of private IP addresses of the private endpoint." - } - } - } - }, - "metadata": { - "description": "The custom DNS configurations of the private endpoint." - } - }, - "networkInterfaceResourceIds": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "The IDs of the network interfaces associated with the private endpoint." - } - } - }, - "metadata": { - "__bicep_export!": true, - "description": "The type for a private endpoint output." - } - }, - "auditSettingsType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Specifies the name of the audit settings." - } - }, - "auditActionsAndGroups": { - "type": "array", - "items": { - "type": "string" - }, - "nullable": true, - "metadata": { - "description": "Optional. Specifies the Actions-Groups and Actions to audit." - } - }, - "isAzureMonitorTargetEnabled": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Specifies whether audit events are sent to Azure Monitor." - } - }, - "isDevopsAuditEnabled": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Specifies the state of devops audit. If state is Enabled, devops logs will be sent to Azure Monitor." - } - }, - "isManagedIdentityInUse": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Specifies whether Managed Identity is used to access blob storage." - } - }, - "isStorageSecondaryKeyInUse": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Specifies whether storageAccountAccessKey value is the storage's secondary key." - } - }, - "queueDelayMs": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. Specifies the amount of time in milliseconds that can elapse before audit actions are forced to be processed." - } - }, - "retentionDays": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. Specifies the number of days to keep in the audit logs in the storage account." - } - }, - "state": { - "type": "string", - "allowedValues": [ - "Disabled", - "Enabled" - ], - "nullable": true, - "metadata": { - "description": "Optional. Specifies the state of the audit. If state is Enabled, storageEndpoint or isAzureMonitorTargetEnabled are required." - } - }, - "storageAccountResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Specifies the identifier key of the auditing storage account." - } - } - }, - "metadata": { - "__bicep_export!": true - } - }, - "secretsExportConfigurationType": { - "type": "object", - "properties": { - "keyVaultResourceId": { - "type": "string", - "metadata": { - "description": "Required. The resource ID of the key vault where to store the secrets of this module." - } - }, - "sqlAdminPasswordSecretName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The sqlAdminPassword secret name to create." - } - }, - "sqlAzureConnectionStringSercretName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The sqlAzureConnectionString secret name to create." - } - } - }, - "metadata": { - "__bicep_export!": true - } - }, - "serverExternalAdministratorType": { - "type": "object", - "properties": { - "administratorType": { - "type": "string", - "allowedValues": [ - "ActiveDirectory" - ], - "nullable": true, - "metadata": { - "description": "Optional. Type of the sever administrator." - } - }, - "azureADOnlyAuthentication": { - "type": "bool", - "metadata": { - "description": "Required. Azure Active Directory only Authentication enabled." - } - }, - "login": { - "type": "string", - "metadata": { - "description": "Required. Login name of the server administrator." - } - }, - "principalType": { - "type": "string", - "allowedValues": [ - "Application", - "Group", - "User" - ], - "metadata": { - "description": "Required. Principal Type of the sever administrator." - } - }, - "sid": { - "type": "string", - "metadata": { - "description": "Required. SID (object ID) of the server administrator." - } - }, - "tenantId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Tenant ID of the administrator." - } - } - }, - "metadata": { - "__bicep_export!": true - } - }, - "databasePropertyType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the Elastic Pool." - } - }, - "tags": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Tags of the resource." - } - }, - "sku": { - "$ref": "#/definitions/databaseSkuType", - "nullable": true, - "metadata": { - "description": "Optional. The database SKU." - } - }, - "autoPauseDelay": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. Time in minutes after which database is automatically paused. A value of -1 means that automatic pause is disabled." - } - }, - "availabilityZone": { - "type": "string", - "allowedValues": [ - "1", - "2", - "3", - "NoPreference" - ], - "nullable": true, - "metadata": { - "description": "Optional. Specifies the availability zone the database is pinned to." - } - }, - "catalogCollation": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Collation of the metadata catalog." - } - }, - "collation": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The collation of the database." - } - }, - "createMode": { - "type": "string", - "allowedValues": [ - "Copy", - "Default", - "OnlineSecondary", - "PointInTimeRestore", - "Recovery", - "Restore", - "RestoreExternalBackup", - "RestoreExternalBackupSecondary", - "RestoreLongTermRetentionBackup", - "Secondary" - ], - "nullable": true, - "metadata": { - "description": "Optional. Specifies the mode of database creation." - } - }, - "elasticPoolResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The resource identifier of the elastic pool containing this database." - } - }, - "encryptionProtector": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The azure key vault URI of the database if it's configured with per Database Customer Managed Keys." - } - }, - "encryptionProtectorAutoRotation": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. The flag to enable or disable auto rotation of database encryption protector AKV key." - } - }, - "federatedClientId": { - "type": "string", - "nullable": true, - "minLength": 36, - "maxLength": 36, - "metadata": { - "description": "Optional. The Client id used for cross tenant per database CMK scenario." - } - }, - "freeLimitExhaustionBehavior": { - "type": "string", - "allowedValues": [ - "AutoPause", - "BillOverUsage" - ], - "nullable": true, - "metadata": { - "description": "Optional. Specifies the behavior when monthly free limits are exhausted for the free database." - } - }, - "highAvailabilityReplicaCount": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The number of secondary replicas associated with the database that are used to provide high availability. Not applicable to a Hyperscale database within an elastic pool." - } - }, - "isLedgerOn": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Whether or not this database is a ledger database, which means all tables in the database are ledger tables." - } - }, - "licenseType": { - "type": "string", - "allowedValues": [ - "BasePrice", - "LicenseIncluded" - ], - "nullable": true, - "metadata": { - "description": "Optional. The license type to apply for this database." - } - }, - "longTermRetentionBackupResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The resource identifier of the long term retention backup associated with create operation of this database." - } - }, - "maintenanceConfigurationId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Maintenance configuration id assigned to the database. This configuration defines the period when the maintenance updates will occur." - } - }, - "manualCutover": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Whether or not customer controlled manual cutover needs to be done during Update Database operation to Hyperscale tier." - } - }, - "maxSizeBytes": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The max size of the database expressed in bytes." - } - }, - "minCapacity": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Minimal capacity that database will always have allocated, if not paused." - } - }, - "performCutover": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. To trigger customer controlled manual cutover during the wait state while Scaling operation is in progress." - } - }, - "preferredEnclaveType": { - "type": "string", - "allowedValues": [ - "Default", - "VBS" - ], - "nullable": true, - "metadata": { - "description": "Optional. Type of enclave requested on the database." - } - }, - "readScale": { - "type": "string", - "allowedValues": [ - "Disabled", - "Enabled" - ], - "nullable": true, - "metadata": { - "description": "Optional. The state of read-only routing. If enabled, connections that have application intent set to readonly in their connection string may be routed to a readonly secondary replica in the same region. Not applicable to a Hyperscale database within an elastic pool." - } - }, - "recoverableDatabaseResourceId": { + "_generator": { + "name": "bicep", + "version": "0.32.4.45862", + "templateHash": "13070013363315850466" + }, + "name": "App Service Plan", + "description": "This module deploys an App Service Plan.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "diagnosticSettingMetricsOnlyType": { + "type": "object", + "properties": { + "name": { "type": "string", "nullable": true, "metadata": { - "description": "Optional. The resource identifier of the recoverable database associated with create operation of this database." + "description": "Optional. The name of diagnostic setting." } }, - "recoveryServicesRecoveryPointResourceId": { - "type": "string", + "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 resource identifier of the recovery point associated with create operation of this database." + "description": "Optional. The name of metrics that will be streamed. \"allMetrics\" includes all possible metrics for the resource. Set to `[]` to disable metric collection." } }, - "requestedBackupStorageRedundancy": { + "logAnalyticsDestinationType": { "type": "string", "allowedValues": [ - "Geo", - "GeoZone", - "Local", - "Zone" + "AzureDiagnostics", + "Dedicated" ], "nullable": true, "metadata": { - "description": "Optional. The storage account type to be used to store backups for this database." + "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." } }, - "restorableDroppedDatabaseResourceId": { + "workspaceResourceId": { "type": "string", "nullable": true, "metadata": { - "description": "Optional. The resource identifier of the restorable dropped database associated with create operation of this database." + "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." } }, - "restorePointInTime": { + "storageAccountResourceId": { "type": "string", "nullable": true, "metadata": { - "description": "Optional. Specifies the point in time (ISO8601 format) of the source database that will be restored to create the new database." + "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." } }, - "sampleName": { + "eventHubAuthorizationRuleResourceId": { "type": "string", "nullable": true, "metadata": { - "description": "Optional. The name of the sample schema to apply when creating this database." + "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." } }, - "secondaryType": { + "eventHubName": { "type": "string", - "allowedValues": [ - "Geo", - "Named", - "Standby" - ], "nullable": true, "metadata": { - "description": "Optional. The secondary type of the database if it is a secondary." + "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." } }, - "sourceDatabaseDeletionDate": { + "marketplacePartnerResourceId": { "type": "string", "nullable": true, "metadata": { - "description": "Optional. Specifies the time that the database was deleted." + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." } - }, - "sourceDatabaseResourceId": { + } + }, + "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.4.1" + } + } + }, + "lockType": { + "type": "object", + "properties": { + "name": { "type": "string", "nullable": true, "metadata": { - "description": "Optional. The resource identifier of the source database associated with create operation of this database." + "description": "Optional. Specify the name of lock." } }, - "sourceResourceId": { + "kind": { "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], "nullable": true, "metadata": { - "description": "Optional. The resource identifier of the source associated with the create operation of this database." - } - }, - "useFreeLimit": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Whether or not the database uses free monthly limits. Allowed on one database in a subscription." - } - }, - "zoneRedundant": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Whether or not this database is zone redundant, which means the replicas of this database will be spread across multiple availability zones." - } - }, - "diagnosticSettings": { - "type": "array", - "items": { - "$ref": "#/definitions/diagnosticSettingFullType" - }, - "nullable": true, - "metadata": { - "description": "Optional. The diagnostic settings of the service." - } - }, - "backupShortTermRetentionPolicy": { - "$ref": "#/definitions/shortTermBackupRetentionPolicyType", - "nullable": true, - "metadata": { - "description": "Optional. The short term backup retention policy for the database." - } - }, - "backupLongTermRetentionPolicy": { - "$ref": "#/definitions/longTermBackupRetentionPolicyType", - "nullable": true, - "metadata": { - "description": "Optional. The long term backup retention policy for the database." + "description": "Optional. Specify the type of lock." } } }, "metadata": { - "__bicep_export!": true + "description": "An AVM-aligned type for a lock.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.1" + } } }, - "elasticPoolPropertyType": { + "roleAssignmentType": { "type": "object", "properties": { "name": { "type": "string", - "metadata": { - "description": "Required. The name of the Elastic Pool." - } - }, - "tags": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Tags of the resource." - } - }, - "sku": { - "$ref": "#/definitions/elasticPoolSkuType", - "nullable": true, - "metadata": { - "description": "Optional. The elastic pool SKU." - } - }, - "autoPauseDelay": { - "type": "int", "nullable": true, "metadata": { - "description": "Optional. Time in minutes after which elastic pool is automatically paused. A value of -1 means that automatic pause is disabled." + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." } }, - "availabilityZone": { + "roleDefinitionIdOrName": { "type": "string", - "allowedValues": [ - "1", - "2", - "3", - "NoPreference" - ], - "nullable": true, "metadata": { - "description": "Optional. Specifies the availability zone the pool's primary replica is pinned to." + "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'." } }, - "highAvailabilityReplicaCount": { - "type": "int", - "nullable": true, + "principalId": { + "type": "string", "metadata": { - "description": "Optional. The number of secondary replicas associated with the elastic pool that are used to provide high availability. Applicable only to Hyperscale elastic pools." + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." } }, - "licenseType": { + "principalType": { "type": "string", "allowedValues": [ - "BasePrice", - "LicenseIncluded" + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" ], "nullable": true, "metadata": { - "description": "Optional. The license type to apply for this elastic pool." + "description": "Optional. The principal type of the assigned principal ID." } }, - "maintenanceConfigurationId": { + "description": { "type": "string", "nullable": true, "metadata": { - "description": "Optional. Maintenance configuration id assigned to the elastic pool. This configuration defines the period when the maintenance updates will will occur." - } - }, - "maxSizeBytes": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The storage limit for the database elastic pool in bytes." - } - }, - "minCapacity": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. Minimal capacity that serverless pool will not shrink below, if not paused." + "description": "Optional. The description of the role assignment." } }, - "perDatabaseSettings": { - "$ref": "#/definitions/elasticPoolPerDatabaseSettingsType", + "condition": { + "type": "string", "nullable": true, "metadata": { - "description": "Optional. The per database settings for the elastic pool." + "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\"." } }, - "preferredEnclaveType": { + "conditionVersion": { "type": "string", "allowedValues": [ - "Default", - "VBS" + "2.0" ], "nullable": true, "metadata": { - "description": "Optional. Type of enclave requested on the elastic pool." + "description": "Optional. Version of the condition." } }, - "zoneRedundant": { - "type": "bool", + "delegatedManagedIdentityResourceId": { + "type": "string", "nullable": true, "metadata": { - "description": "Optional. Whether or not this elastic pool is zone redundant, which means the replicas of this elastic pool will be spread across multiple availability zones." + "description": "Optional. The Resource Id of the delegated managed identity resource." } } }, "metadata": { - "__bicep_export!": true + "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.4.1" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "minLength": 1, + "maxLength": 60, + "metadata": { + "description": "Required. Name of the app service plan." } }, - "encryptionProtectorType": { - "type": "object", - "properties": { - "serverKeyName": { - "type": "string", - "metadata": { - "description": "Required. The name of the server key." - } - }, - "serverKeyType": { - "type": "string", - "allowedValues": [ - "AzureKeyVault", - "ServiceManaged" - ], - "nullable": true, - "metadata": { - "description": "Optional. The encryption protector type." - } - }, - "autoRotationEnabled": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Key auto rotation opt-in flag." - } - } + "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." + } + }, + "appServiceEnvironmentId": { + "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": { + "$ref": "#/definitions/lockType", + "nullable": true, + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" }, + "nullable": true, "metadata": { - "__bicep_export!": true + "description": "Optional. Array of role assignments to create." } }, - "vulnerabilityAssessmentType": { + "tags": { "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "diagnosticSettings": { + "type": "array", + "items": { + "$ref": "#/definitions/diagnosticSettingMetricsOnlyType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + } + }, + "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')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]", + "Web Plan Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2cc479cb-7b4d-49a8-b449-8c00fd0f0a4b')]", + "Website Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'de139f84-1756-47ae-9be6-808fbbe84772')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.web-serverfarm.{0}.{1}', replace('0.4.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the vulnerability assessment." - } - }, - "recurringScans": { - "$ref": "#/definitions/recurringScansType", - "nullable": true, - "metadata": { - "description": "Optional. The recurring scans settings." - } - }, - "storageAccountResourceId": { - "type": "string", - "metadata": { - "description": "Required. The resource ID of the storage account to store the scan reports." - } - }, - "useStorageAccountAccessKey": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Specifies whether to use the storage account access key to access the storage account." - } - }, - "createStorageRoleAssignment": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Specifies whether to create a role assignment for the storage account." + "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" + } } } - }, - "metadata": { - "__bicep_export!": true } }, - "firewallRuleType": { - "type": "object", + "appServicePlan": { + "type": "Microsoft.Web/serverfarms", + "apiVersion": "2022-09-01", + "name": "[parameters('name')]", + "kind": "[parameters('kind')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "sku": { + "name": "[parameters('skuName')]", + "capacity": "[if(equals(parameters('skuName'), 'FC1'), null(), parameters('skuCapacity'))]", + "tier": "[if(equals(parameters('skuName'), 'FC1'), 'FlexConsumption', null())]" + }, "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the firewall rule." - } - }, - "startIpAddress": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The start IP address of the firewall rule. Must be IPv4 format. Use value '0.0.0.0' for all Azure-internal IP addresses." - } - }, - "endIpAddress": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The end IP address of the firewall rule. Must be IPv4 format. Must be greater than or equal to startIpAddress. Use value '0.0.0.0' for all Azure-internal IP addresses." + "workerTierName": "[parameters('workerTierName')]", + "hostingEnvironmentProfile": "[if(not(empty(parameters('appServiceEnvironmentId'))), createObject('id', parameters('appServiceEnvironmentId')), null())]", + "perSiteScaling": "[parameters('perSiteScaling')]", + "maximumElasticWorkerCount": "[parameters('maximumElasticWorkerCount')]", + "elasticScaleEnabled": "[parameters('elasticScaleEnabled')]", + "reserved": "[parameters('reserved')]", + "targetWorkerCount": "[parameters('targetWorkerCount')]", + "targetWorkerSizeId": "[parameters('targetWorkerSize')]", + "zoneRedundant": "[parameters('zoneRedundant')]" + } + }, + "appServicePlan_diagnosticSettings": { + "copy": { + "name": "appServicePlan_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Web/serverfarms/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null + } } - } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "appServicePlan" + ] + }, + "appServicePlan_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.Web/serverfarms/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "appServicePlan" + ] + }, + "appServicePlan_roleAssignments": { + "copy": { + "name": "appServicePlan_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Web/serverfarms/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Web/serverfarms', 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": [ + "appServicePlan" + ] + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", "metadata": { - "__bicep_export!": true - } + "description": "The resource group the app service plan was deployed into." + }, + "value": "[resourceGroup().name]" }, - "keyType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the key. Must follow the [__] pattern." - } - }, - "serverKeyType": { - "type": "string", - "allowedValues": [ - "AzureKeyVault", - "ServiceManaged" - ], - "nullable": true, - "metadata": { - "description": "Optional. The server key type." - } - }, - "uri": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The URI of the server key. If the ServerKeyType is AzureKeyVault, then the URI is required. The AKV URI is required to be in this format: 'https://YourVaultName.azure.net/keys/YourKeyName/YourKeyVersion'." - } - } + "name": { + "type": "string", + "metadata": { + "description": "The name of the app service plan." }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", "metadata": { - "__bicep_export!": true - } + "description": "The resource ID of the app service plan." + }, + "value": "[resourceId('Microsoft.Web/serverfarms', parameters('name'))]" }, - "virtualNetworkRuleType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the Server Virtual Network Rule." - } - }, - "virtualNetworkSubnetId": { - "type": "string", - "metadata": { - "description": "Required. The resource ID of the virtual network subnet." + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('appServicePlan', '2022-09-01', 'full').location]" + } + } + } + } + }, + "appService": { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('{0}-app-service-deployment', variables('nameFormatted')), 64)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[variables('nameFormatted')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "kind": { + "value": "app,linux,container" + }, + "serverFarmResourceId": { + "value": "[reference('appServicePlan').outputs.resourceId.value]" + }, + "appInsightResourceId": { + "value": "[resourceId('Microsoft.Insights/components', parameters('appInsightsName'))]" + }, + "virtualNetworkSubnetId": { + "value": "[parameters('virtualNetworkSubnetId')]" + }, + "keyVaultAccessIdentityResourceId": { + "value": "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('userAssignedIdentityName'))]" + }, + "managedIdentities": { + "value": { + "userAssignedResourceIds": [ + "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('userAssignedIdentityName'))]" + ] + } + }, + "logsConfiguration": { + "value": { + "applicationLogs": { + "fileSystem": { + "level": "Information" + } + }, + "detailedErrorMessages": { + "enabled": true + }, + "failedRequestsTracing": { + "enabled": true + }, + "httpLogs": { + "fileSystem": { + "enabled": true, + "retentionInDays": 1, + "retentionInMb": 35 + } + } + } + }, + "diagnosticSettings": { + "value": [ + { + "workspaceResourceId": "[parameters('logAnalyticsWorkspaceResourceId')]", + "metricCategories": [ + { + "category": "AllMetrics" } - }, - "ignoreMissingVnetServiceEndpoint": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Allow creating a firewall rule before the virtual network has vnet service endpoint enabled." + ] + } + ] + }, + "httpsOnly": { + "value": true + }, + "publicNetworkAccess": { + "value": "Enabled" + }, + "authSettingV2Configuration": { + "value": { + "globalValidation": { + "requireAuthentication": true, + "unauthenticatedClientAction": "RedirectToLoginPage", + "redirectToProvider": "azureactivedirectory" + }, + "identityProviders": { + "azureActiveDirectory": { + "enabled": true, + "registration": { + "clientId": "[parameters('authProvider').clientId]", + "clientSecretSettingName": "AUTH_CLIENT_SECRET", + "openIdIssuer": "[parameters('authProvider').openIdIssuer]" + }, + "validation": { + "defaultAuthorizationPolicy": { + "allowedApplications": [] + } } } }, - "metadata": { - "__bicep_export!": true + "login": { + "tokenStore": { + "enabled": true + } } + } + }, + "appSettingsKeyValuePairs": { + "value": { + "APPINSIGHTS_INSTRUMENTATIONKEY": "[reference('appInsights').InstrumentationKey]", + "APPINSIGHTS_PROFILERFEATURE_VERSION": "1.0.0", + "APPINSIGHTS_SNAPSHOTFEATURE_VERSION": "1.0.0", + "APPLICATIONINSIGHTS_CONFIGURATION_CONTENT": "", + "APPLICATIONINSIGHTS_CONNECTION_STRING": "[reference('appInsights').ConnectionString]", + "ApplicationInsightsAgent_EXTENSION_VERSION": "~3", + "AUTH_CLIENT_SECRET": "[format('@Microsoft.KeyVault(VaultName={0};SecretName={1})', parameters('keyVaultName'), parameters('authProvider').clientSecretName)]", + "AZURE_CLIENT_ID": "[reference('userAssignedIdentity').clientId]", + "AZURE_COSMOSDB_ACCOUNT": "[parameters('cosmosDbConfiguration').account]", + "AZURE_COSMOSDB_CONVERSATIONS_CONTAINER": "[parameters('cosmosDbConfiguration').container]", + "AZURE_COSMOSDB_DATABASE": "[parameters('cosmosDbConfiguration').database]", + "AZURE_COSMOSDB_MONGO_VCORE_CONNECTION_STRING": "", + "AZURE_COSMOSDB_MONGO_VCORE_CONTAINER": "", + "AZURE_COSMOSDB_MONGO_VCORE_CONTENT_COLUMNS": "content", + "AZURE_COSMOSDB_MONGO_VCORE_DATABASE": "", + "AZURE_COSMOSDB_MONGO_VCORE_FILENAME_COLUMN": "filepath", + "AZURE_COSMOSDB_MONGO_VCORE_INDEX": "", + "AZURE_COSMOSDB_MONGO_VCORE_TITLE_COLUMN": "title", + "AZURE_COSMOSDB_MONGO_VCORE_URL_COLUMN": "url", + "AZURE_COSMOSDB_MONGO_VCORE_VECTOR_COLUMNS": "contentVector", + "AZURE_OPENAI_EMBEDDING_ENDPOINT": "", + "AZURE_OPENAI_EMBEDDING_KEY": "", + "AZURE_OPENAI_EMBEDDING_NAME": "[parameters('openAIConfiguration').embeddingModelDeploymentName]", + "AZURE_OPENAI_ENDPOINT": "[parameters('openAIConfiguration').endpoint]", + "AZURE_OPENAI_KEY": "", + "AZURE_OPENAI_MAX_TOKENS": "800", + "AZURE_OPENAI_MODEL": "[parameters('openAIConfiguration').gptModelName]", + "AZURE_OPENAI_MODEL_NAME": "[parameters('openAIConfiguration').gptModelDeploymentName]", + "AZURE_OPENAI_RESOURCE": "[parameters('openAIConfiguration').name]", + "AZURE_OPENAI_STOP_SEQUENCE": "", + "AZURE_OPENAI_SYSTEM_MESSAGE": "You are an AI assistant that helps people find information.", + "AZURE_OPENAI_TEMPERATURE": "0.7", + "AZURE_OPENAI_TOP_P": "0.95", + "AZURE_SEARCH_CONTENT_COLUMNS": "content", + "AZURE_SEARCH_ENABLE_IN_DOMAIN": "true", + "AZURE_SEARCH_FILENAME_COLUMN": "filepath", + "AZURE_SEARCH_INDEX": "[parameters('searchServiceConfiguration').indexName]", + "AZURE_SEARCH_KEY": "", + "AZURE_SEARCH_PERMITTED_GROUPS_COLUMN": "", + "AZURE_SEARCH_QUERY_TYPE": "vector_simple_hybrid", + "AZURE_SEARCH_SEMANTIC_SEARCH_CONFIG": "azureml-default", + "AZURE_SEARCH_SERVICE": "[parameters('searchServiceConfiguration').name]", + "AZURE_SEARCH_STRICTNESS": "3", + "AZURE_SEARCH_TITLE_COLUMN": "title", + "AZURE_SEARCH_TOP_K": "5", + "AZURE_SEARCH_URL_COLUMN": "url", + "AZURE_SEARCH_USE_SEMANTIC_SEARCH": "true", + "AZURE_SEARCH_VECTOR_COLUMNS": "contentVector", + "DATASOURCE_TYPE": "AzureCognitiveSearch", + "DiagnosticServices_EXTENSION_VERSION": "~3", + "ELASTICSEARCH_CONTENT_COLUMNS": "", + "ELASTICSEARCH_EMBEDDING_MODEL_ID": "", + "ELASTICSEARCH_ENABLE_IN_DOMAIN": "true", + "ELASTICSEARCH_ENCODED_API_KEY": "", + "ELASTICSEARCH_ENDPOINT": "", + "ELASTICSEARCH_FILENAME_COLUMN": "filepath", + "ELASTICSEARCH_INDEX": "", + "ELASTICSEARCH_QUERY_TYPE": "", + "ELASTICSEARCH_STRICTNESS": "3", + "ELASTICSEARCH_TITLE_COLUMN": "title", + "ELASTICSEARCH_TOP_K": "5", + "ELASTICSEARCH_URL_COLUMN": "url", + "ELASTICSEARCH_VECTOR_COLUMNS": "contentVector", + "InstrumentationEngine_EXTENSION_VERSION": "disabled", + "MONGODB_APP_NAME": "", + "MONGODB_COLLECTION_NAME": "", + "MONGODB_CONTENT_COLUMNS": "", + "MONGODB_DATABASE_NAME": "", + "MONGODB_ENABLE_IN_DOMAIN": "true", + "MONGODB_ENDPOINT": "", + "MONGODB_FILENAME_COLUMN": "", + "MONGODB_INDEX_NAME": "", + "MONGODB_PASSWORD": "", + "MONGODB_STRICTNESS": "3", + "MONGODB_TITLE_COLUMN": "", + "MONGODB_TOP_K": "5", + "MONGODB_URL_COLUMN": "", + "MONGODB_USERNAME": "", + "MONGODB_VECTOR_COLUMNS": "", + "SCM_DO_BUILD_DURING_DEPLOYMENT": "true", + "SnapshotDebugger_EXTENSION_VERSION": "disabled", + "XDT_MicrosoftApplicationInsights_BaseExtensions": "disabled", + "XDT_MicrosoftApplicationInsights_Mode": "recommended", + "XDT_MicrosoftApplicationInsights_PreemptSdk": "disabled" + } + }, + "siteConfig": { + "value": { + "alwaysOn": true, + "ftpsState": "FtpsOnly", + "linuxFxVersion": "[format('DOCKER|{0}:{1}', parameters('imagePath'), parameters('imageTag'))]" + } + } + }, + "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.33.93.31351", + "templateHash": "2522527858358792357" }, - "securityAlerPolicyType": { + "name": "Web/Function Apps", + "description": "This module deploys a Web or Function App." + }, + "definitions": { + "privateEndpointOutputType": { "type": "object", "properties": { "name": { "type": "string", "metadata": { - "description": "Required. The name of the Security Alert Policy." - } - }, - "disabledAlerts": { - "type": "array", - "allowedValues": [ - "Access_Anomaly", - "Brute_Force", - "Data_Exfiltration", - "Sql_Injection", - "Sql_Injection_Vulnerability", - "Unsafe_Action" - ], - "nullable": true, - "metadata": { - "description": "Optional. Alerts to disable." - } - }, - "emailAccountAdmins": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Specifies that the alert is sent to the account administrators." - } - }, - "emailAddresses": { - "type": "array", - "items": { - "type": "string" - }, - "nullable": true, - "metadata": { - "description": "Optional. Specifies an array of email addresses to which the alert is sent." - } - }, - "retentionDays": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. Specifies the number of days to keep in the Threat Detection audit logs." - } - }, - "state": { - "type": "string", - "allowedValues": [ - "Disabled", - "Enabled" - ], - "nullable": true, - "metadata": { - "description": "Optional. Specifies the state of the policy, whether it is enabled or disabled or a policy has not been applied yet on the specific database." + "description": "The name of the private endpoint." } }, - "storageAccountAccessKey": { + "resourceId": { "type": "string", - "nullable": true, "metadata": { - "description": "Optional. Specifies the identifier key of the Threat Detection audit storage account." + "description": "The resource ID of the private endpoint." } }, - "storageEndpoint": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Specifies the blob storage endpoint. This blob storage will hold all Threat Detection audit logs." - } - } - }, - "metadata": { - "__bicep_export!": true - } - }, - "failoverGroupType": { - "type": "object", - "properties": { - "name": { + "groupId": { "type": "string", - "metadata": { - "description": "Required. The name of the failover group." - } - }, - "tags": { - "type": "object", "nullable": true, "metadata": { - "description": "Optional. Tags of the resource." + "description": "The group Id for the private endpoint Group." } }, - "databases": { + "customDnsConfigs": { "type": "array", "items": { - "type": "string" + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "FQDN that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "A list of private IP addresses of the private endpoint." + } + } + } }, "metadata": { - "description": "Required. List of databases in the failover group." + "description": "The custom DNS configurations of the private endpoint." } }, - "partnerServers": { + "networkInterfaceResourceIds": { "type": "array", "items": { "type": "string" }, "metadata": { - "description": "Required. List of the partner servers for the failover group." - } - }, - "readOnlyEndpoint": { - "$ref": "#/definitions/failoverGroupReadOnlyEndpointType", - "nullable": true, - "metadata": { - "description": "Optional. Read-only endpoint of the failover group instance." - } - }, - "readWriteEndpoint": { - "$ref": "#/definitions/failoverGroupReadWriteEndpointType", - "metadata": { - "description": "Required. Read-write endpoint of the failover group instance." - } - }, - "secondaryType": { - "type": "string", - "allowedValues": [ - "Geo", - "Standby" - ], - "metadata": { - "description": "Required. Databases secondary type on partner server." + "description": "The IDs of the network interfaces associated with the private endpoint." } } }, @@ -81989,124 +93845,6 @@ } } }, - "_1.secretSetOutputType": { - "type": "object", - "properties": { - "secretResourceId": { - "type": "string", - "metadata": { - "description": "The resourceId of the exported secret." - } - }, - "secretUri": { - "type": "string", - "metadata": { - "description": "The secret URI of the exported secret." - } - }, - "secretUriWithVersion": { - "type": "string", - "metadata": { - "description": "The secret URI with version of the exported secret." - } - } - }, - "metadata": { - "description": "An AVM-aligned type for the output of the secret set via the secrets export feature.", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } - } - }, - "customerManagedKeyWithAutoRotateType": { - "type": "object", - "properties": { - "keyVaultResourceId": { - "type": "string", - "metadata": { - "description": "Required. The resource ID of a key vault to reference a customer managed key for encryption from." - } - }, - "keyName": { - "type": "string", - "metadata": { - "description": "Required. The name of the customer managed key to use for encryption." - } - }, - "keyVersion": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The version of the customer managed key to reference for encryption. If not provided, using version as per 'autoRotationEnabled' setting." - } - }, - "autoRotationEnabled": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Enable or disable auto-rotating to the latest key version. Default is `true`. If set to `false`, the latest key version at the time of the deployment is used." - } - }, - "userAssignedIdentityResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use." - } - } - }, - "metadata": { - "description": "An AVM-aligned type for a customer-managed key. To be used if the resource type supports auto-rotation of the customer-managed key.", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } - } - }, - "databaseSkuType": { - "type": "object", - "properties": { - "capacity": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The capacity of the particular SKU." - } - }, - "family": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. If the service has different generations of hardware, for the same SKU, then that can be captured here." - } - }, - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the SKU, typically, a letter + Number code, e.g. P3." - } - }, - "size": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Size of the particular SKU." - } - }, - "tier": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The tier or edition of the particular SKU, e.g. Basic, Premium." - } - } - }, - "metadata": { - "description": "The database SKU.", - "__bicep_imported_from!": { - "sourceTemplate": "database/main.bicep" - } - } - }, "diagnosticSettingFullType": { "type": "object", "properties": { @@ -82169,205 +93907,63 @@ } } } - }, - "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 both logs & metrics are supported by the resource provider.", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } - } - }, - "elasticPoolPerDatabaseSettingsType": { - "type": "object", - "properties": { - "autoPauseDelay": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. Auto Pause Delay for per database within pool." - } - }, - "maxCapacity": { - "type": "string", - "metadata": { - "description": "Required. The maximum capacity any one database can consume. Examples: '0.5', '2'." - } - }, - "minCapacity": { - "type": "string", - "metadata": { - "description": "Required. The minimum capacity all databases are guaranteed. Examples: '0.5', '1'." - } - } - }, - "metadata": { - "description": "The per database settings for the elastic pool.", - "__bicep_imported_from!": { - "sourceTemplate": "elastic-pool/main.bicep" - } - } - }, - "elasticPoolSkuType": { - "type": "object", - "properties": { - "capacity": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The capacity of the particular SKU." - } - }, - "family": { - "type": "string", + }, "nullable": true, "metadata": { - "description": "Optional. If the service has different generations of hardware, for the same SKU, then that can be captured here." + "description": "Optional. The name of metrics that will be streamed. \"allMetrics\" includes all possible metrics for the resource. Set to `[]` to disable metric collection." } }, - "name": { + "logAnalyticsDestinationType": { "type": "string", "allowedValues": [ - "BC_DC", - "BC_Gen5", - "BasicPool", - "GP_DC", - "GP_FSv2", - "GP_Gen5", - "HS_Gen5", - "HS_MOPRMS", - "HS_PRMS", - "PremiumPool", - "ServerlessPool", - "StandardPool" + "AzureDiagnostics", + "Dedicated" ], + "nullable": true, "metadata": { - "description": "Required. The name of the SKU, typically, a letter + Number code, e.g. P3." + "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." } }, - "size": { + "workspaceResourceId": { "type": "string", "nullable": true, "metadata": { - "description": "Optional. Size of the particular SKU." + "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." } }, - "tier": { + "storageAccountResourceId": { "type": "string", "nullable": true, "metadata": { - "description": "Optional. The tier or edition of the particular SKU, e.g. Basic, Premium." - } - } - }, - "metadata": { - "description": "The elastic pool SKU.", - "__bicep_imported_from!": { - "sourceTemplate": "elastic-pool/main.bicep" - } - } - }, - "failoverGroupReadOnlyEndpointType": { - "type": "object", - "properties": { - "failoverPolicy": { - "type": "string", - "allowedValues": [ - "Disabled", - "Enabled" - ], - "metadata": { - "description": "Required. Failover policy of the read-only endpoint for the failover group." + "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." } }, - "targetServer": { + "eventHubAuthorizationRuleResourceId": { "type": "string", + "nullable": true, "metadata": { - "description": "Required. The target partner server where the read-only endpoint points to." + "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." } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "failover-group/main.bicep" - } - } - }, - "failoverGroupReadWriteEndpointType": { - "type": "object", - "properties": { - "failoverPolicy": { + }, + "eventHubName": { "type": "string", - "allowedValues": [ - "Automatic", - "Manual" - ], + "nullable": true, "metadata": { - "description": "Required. Failover policy of the read-write endpoint for the failover group. If failoverPolicy is Automatic then failoverWithDataLossGracePeriodMinutes is required." + "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." } }, - "failoverWithDataLossGracePeriodMinutes": { - "type": "int", + "marketplacePartnerResourceId": { + "type": "string", "nullable": true, "metadata": { - "description": "Optional. Grace period before failover with data loss is attempted for the read-write endpoint." + "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 both logs & metrics are supported by the resource provider.", "__bicep_imported_from!": { - "sourceTemplate": "failover-group/main.bicep" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, @@ -82401,63 +93997,6 @@ } } }, - "longTermBackupRetentionPolicyType": { - "type": "object", - "properties": { - "backupStorageAccessTier": { - "type": "string", - "allowedValues": [ - "Archive", - "Hot" - ], - "nullable": true, - "metadata": { - "description": "Optional. The BackupStorageAccessTier for the LTR backups." - } - }, - "makeBackupsImmutable": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. The setting whether to make LTR backups immutable." - } - }, - "monthlyRetention": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Monthly retention in ISO 8601 duration format." - } - }, - "weeklyRetention": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Weekly retention in ISO 8601 duration format." - } - }, - "weekOfYear": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. Week of year backup to keep for yearly retention." - } - }, - "yearlyRetention": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Yearly retention in ISO 8601 duration format." - } - } - }, - "metadata": { - "description": "The long-term backup retention policy for the database.", - "__bicep_imported_from!": { - "sourceTemplate": "database/main.bicep" - } - } - }, "managedIdentityAllType": { "type": "object", "properties": { @@ -82628,39 +94167,6 @@ } } }, - "recurringScansType": { - "type": "object", - "properties": { - "emails": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. Specifies an array of e-mail addresses to which the scan notification is sent." - } - }, - "emailSubscriptionAdmins": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Specifies that the schedule scan notification will be sent to the subscription administrators." - } - }, - "isEnabled": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Recurring scans state." - } - } - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "vulnerability-assessment/main.bicep" - } - } - }, "roleAssignmentType": { "type": "object", "properties": { @@ -82735,75 +94241,74 @@ "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } - }, - "secretsOutputType": { - "type": "object", - "properties": {}, - "additionalProperties": { - "$ref": "#/definitions/_1.secretSetOutputType", - "metadata": { - "description": "An exported secret's references." - } - }, + } + }, + "parameters": { + "name": { + "type": "string", "metadata": { - "description": "A map of the exported secrets", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } + "description": "Required. Name of the site." } }, - "shortTermBackupRetentionPolicyType": { - "type": "object", - "properties": { - "diffBackupIntervalInHours": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. Differential backup interval in hours. For Hyperscale tiers this value will be ignored." - } - }, - "retentionDays": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. Point-in-time retention in days." - } - } - }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", "metadata": { - "description": "The short-term backup retention policy for the database.", - "__bicep_imported_from!": { - "sourceTemplate": "database/main.bicep" - } + "description": "Optional. Location for all Resources." } - } - }, - "parameters": { - "administratorLogin": { + }, + "kind": { "type": "string", - "defaultValue": "", + "allowedValues": [ + "functionapp", + "functionapp,linux", + "functionapp,workflowapp", + "functionapp,workflowapp,linux", + "functionapp,linux,container", + "functionapp,linux,container,azurecontainerapps", + "app,linux", + "app", + "linux,api", + "api", + "app,linux,container", + "app,container,windows" + ], "metadata": { - "description": "Conditional. The administrator username for the server. Required if no `administrators` object for AAD authentication is provided." + "description": "Required. Type of site to deploy." } }, - "administratorLoginPassword": { - "type": "securestring", - "defaultValue": "", + "serverFarmResourceId": { + "type": "string", "metadata": { - "description": "Conditional. The administrator login password. Required if no `administrators` object for AAD authentication is provided." + "description": "Required. The resource ID of the app service plan to use for the site." } }, - "location": { + "managedEnvironmentId": { "type": "string", - "defaultValue": "[resourceGroup().location]", + "nullable": true, "metadata": { - "description": "Optional. Location for all resources." + "description": "Optional. Azure Resource Manager ID of the customers selected Managed Environment on which to host this app." } }, - "name": { + "httpsOnly": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Configures a site to accept only HTTPS requests. Issues redirect for HTTP requests." + } + }, + "clientAffinityEnabled": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. If client affinity is enabled." + } + }, + "appServiceEnvironmentResourceId": { "type": "string", + "nullable": true, "metadata": { - "description": "Required. The name of the server." + "description": "Optional. The resource ID of the app service environment to use for this resource." } }, "managedIdentities": { @@ -82813,149 +94318,134 @@ "description": "Optional. The managed identity definition for this resource." } }, - "primaryUserAssignedIdentityId": { + "keyVaultAccessIdentityResourceId": { "type": "string", - "defaultValue": "", + "nullable": true, "metadata": { - "description": "Conditional. The resource ID of a user assigned identity to be used by default. Required if \"userAssignedIdentities\" is not empty." + "description": "Optional. The resource ID of the assigned identity to be used to access a key vault with." } }, - "lock": { - "$ref": "#/definitions/lockType", - "nullable": true, + "storageAccountRequired": { + "type": "bool", + "defaultValue": false, "metadata": { - "description": "Optional. The lock settings of the service." + "description": "Optional. Checks if Customer provided storage account is required." } }, - "roleAssignments": { - "type": "array", - "items": { - "$ref": "#/definitions/roleAssignmentType" - }, + "virtualNetworkSubnetId": { + "type": "string", "nullable": true, "metadata": { - "description": "Optional. Array of role assignments to create." + "description": "Optional. Azure Resource Manager ID of the Virtual network and subnet to be joined by Regional VNET Integration. This must be of the form /subscriptions/{subscriptionName}/resourceGroups/{resourceGroupName}/providers/Microsoft.Network/virtualNetworks/{vnetName}/subnets/{subnetName}." } }, - "tags": { - "type": "object", - "nullable": true, + "vnetContentShareEnabled": { + "type": "bool", + "defaultValue": false, "metadata": { - "description": "Optional. Tags of the resource." + "description": "Optional. To enable accessing content over virtual network." } }, - "enableTelemetry": { + "vnetImagePullEnabled": { "type": "bool", - "defaultValue": true, + "defaultValue": false, "metadata": { - "description": "Optional. Enable/Disable usage telemetry for module." + "description": "Optional. To enable pulling image over Virtual Network." } }, - "databases": { - "type": "array", - "items": { - "$ref": "#/definitions/databasePropertyType" - }, - "defaultValue": [], + "vnetRouteAllEnabled": { + "type": "bool", + "defaultValue": false, "metadata": { - "description": "Optional. The databases to create in the server." + "description": "Optional. Virtual Network Route All enabled. This causes all outbound traffic to have Virtual Network Security Groups and User Defined Routes applied." } }, - "elasticPools": { - "type": "array", - "items": { - "$ref": "#/definitions/elasticPoolPropertyType" - }, - "defaultValue": [], + "scmSiteAlsoStopped": { + "type": "bool", + "defaultValue": false, "metadata": { - "description": "Optional. The Elastic Pools to create in the server." + "description": "Optional. Stop SCM (KUDU) site when the app is stopped." } }, - "firewallRules": { - "type": "array", - "items": { - "$ref": "#/definitions/firewallRuleType" + "siteConfig": { + "type": "object", + "defaultValue": { + "alwaysOn": true, + "minTlsVersion": "1.2", + "ftpsState": "FtpsOnly" }, - "defaultValue": [], "metadata": { - "description": "Optional. The firewall rules to create in the server." + "description": "Optional. The site config object. The defaults are set to the following values: alwaysOn: true, minTlsVersion: '1.2', ftpsState: 'FtpsOnly'." } }, - "virtualNetworkRules": { - "type": "array", - "items": { - "$ref": "#/definitions/virtualNetworkRuleType" - }, - "defaultValue": [], + "functionAppConfig": { + "type": "object", + "nullable": true, "metadata": { - "description": "Optional. The virtual network rules to create in the server." + "description": "Optional. The Function App configuration object." } }, - "securityAlertPolicies": { - "type": "array", - "items": { - "$ref": "#/definitions/securityAlerPolicyType" - }, - "defaultValue": [], + "storageAccountResourceId": { + "type": "string", + "nullable": true, "metadata": { - "description": "Optional. The security alert policies to create in the server." + "description": "Optional. Required if app of kind functionapp. Resource ID of the storage account to manage triggers and logging function executions." } }, - "keys": { - "type": "array", - "items": { - "$ref": "#/definitions/keyType" - }, - "defaultValue": [], + "storageAccountUseIdentityAuthentication": { + "type": "bool", + "defaultValue": false, "metadata": { - "description": "Optional. The keys to configure." + "description": "Optional. If the provided storage account requires Identity based authentication ('allowSharedKeyAccess' is set to false). When set to true, the minimum role assignment required for the App Service Managed Identity to the storage account is 'Storage Blob Data Owner'." } }, - "customerManagedKey": { - "$ref": "#/definitions/customerManagedKeyWithAutoRotateType", + "webConfiguration": { + "type": "object", "nullable": true, "metadata": { - "description": "Optional. The customer managed key definition for server TDE." + "description": "Optional. The Site Config, Web settings to deploy." } }, - "administrators": { - "$ref": "#/definitions/serverExternalAdministratorType", + "msDeployConfiguration": { + "type": "object", "nullable": true, "metadata": { - "description": "Conditional. The Azure Active Directory (AAD) administrator authentication. Required if no `administratorLogin` & `administratorLoginPassword` is provided." + "description": "Optional. The extension MSDeployment configuration." } }, - "federatedClientId": { + "appInsightResourceId": { "type": "string", "nullable": true, - "minLength": 36, - "maxLength": 36, "metadata": { - "description": "Optional. The Client id used for cross tenant CMK scenario." + "description": "Optional. Resource ID of the app insight to leverage for this resource." } }, - "minimalTlsVersion": { - "type": "string", - "defaultValue": "1.2", - "allowedValues": [ - "1.0", - "1.1", - "1.2", - "1.3" - ], + "appSettingsKeyValuePairs": { + "type": "object", + "nullable": true, "metadata": { - "description": "Optional. Minimal TLS version allowed." + "description": "Optional. The app settings-value pairs except for AzureWebJobsStorage, AzureWebJobsDashboard, APPINSIGHTS_INSTRUMENTATIONKEY and APPLICATIONINSIGHTS_CONNECTION_STRING." } }, - "isIPv6Enabled": { - "type": "string", - "defaultValue": "Disabled", - "allowedValues": [ - "Disabled", - "Enabled" - ], + "authSettingV2Configuration": { + "type": "object", + "nullable": true, "metadata": { - "description": "Optional. Whether or not to enable IPv6 support for this server." + "description": "Optional. The auth settings V2 configuration." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "nullable": true, + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "logsConfiguration": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The logs settings configuration." } }, "privateEndpoints": { @@ -82968,1284 +94458,460 @@ "description": "Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible." } }, - "publicNetworkAccess": { - "type": "string", - "defaultValue": "", - "allowedValues": [ - "", - "Enabled", - "Disabled", - "SecuredByPerimeter" - ], + "slots": { + "type": "array", + "nullable": true, "metadata": { - "description": "Optional. Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set and neither firewall rules nor virtual network rules are set." + "description": "Optional. Configuration for deployment slots for an app." } }, - "restrictOutboundNetworkAccess": { - "type": "string", - "defaultValue": "", - "allowedValues": [ - "", - "Enabled", - "Disabled" - ], + "tags": { + "type": "object", + "nullable": true, "metadata": { - "description": "Optional. Whether or not to restrict outbound network access for this server." + "description": "Optional. Tags of the resource." } }, - "vulnerabilityAssessmentsObj": { - "$ref": "#/definitions/vulnerabilityAssessmentType", + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, "nullable": true, "metadata": { - "description": "Optional. The vulnerability assessment configuration." + "description": "Optional. Array of role assignments to create." } }, - "auditSettings": { - "$ref": "#/definitions/auditSettingsType", - "defaultValue": { - "state": "Enabled" + "diagnosticSettings": { + "type": "array", + "items": { + "$ref": "#/definitions/diagnosticSettingFullType" }, + "nullable": true, "metadata": { - "description": "Optional. The audit settings configuration. If you want to disable auditing, set the parmaeter to an empty object." + "description": "Optional. The diagnostic settings of the service." } }, - "secretsExportConfiguration": { - "$ref": "#/definitions/secretsExportConfigurationType", + "clientCertEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. To enable client certificate authentication (TLS mutual authentication)." + } + }, + "clientCertExclusionPaths": { + "type": "string", "nullable": true, "metadata": { - "description": "Optional. Key vault reference and secret settings for the module's secrets export." + "description": "Optional. Client certificate authentication comma-separated exclusion paths." } }, - "failoverGroups": { - "type": "array", - "items": { - "$ref": "#/definitions/failoverGroupType" - }, - "defaultValue": [], + "clientCertMode": { + "type": "string", + "defaultValue": "Optional", + "allowedValues": [ + "Optional", + "OptionalInteractiveUser", + "Required" + ], "metadata": { - "description": "Optional. The failover groups configuration." + "description": "Optional. This composes with ClientCertEnabled setting.\n- ClientCertEnabled=false means ClientCert is ignored.\n- ClientCertEnabled=true and ClientCertMode=Required means ClientCert is required.\n- ClientCertEnabled=true and ClientCertMode=Optional means ClientCert is optional or accepted.\n" } - } - }, - "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)))))]" + }, + "cloningInfo": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. If specified during app creation, the app is cloned from a source app." } - ], - "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", - "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned,UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', null())), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", - "enableReferencedModulesTelemetry": false, - "builtInRoleNames": { - "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", - "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", - "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Reservation Purchaser": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f7b75c60-3036-4b75-91c3-6b41c27c1689')]", - "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "SQL DB Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9b7fa17d-e63e-47b0-bb0a-15c516ac86ec')]", - "SQL Managed Instance Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4939a1f6-9ae0-4e48-a1e0-f2cbe897382d')]", - "SQL Security Manager": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '056cd41c-7e88-42e1-933e-88ba6a50c9c3')]", - "SQL Server Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6d8ee4ec-f05a-4a1d-8b00-a9b17e38b437')]", - "SqlDb Migration Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '189207d4-bb67-4208-a635-b06afe8b2c57')]", - "SqlMI Migration Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1d335eef-eee1-47fe-a9e0-53214eba8872')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" - } - }, - "resources": { - "cMKKeyVault::cMKKey": { - "condition": "[and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), not(empty(tryGet(parameters('customerManagedKey'), 'keyName')))))]", - "existing": true, - "type": "Microsoft.KeyVault/vaults/keys", - "apiVersion": "2023-07-01", - "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[2]]", - "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[4]]", - "name": "[format('{0}/{1}', last(split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')), tryGet(parameters('customerManagedKey'), 'keyName'))]" }, - "cMKKeyVault": { - "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId')))]", - "existing": true, - "type": "Microsoft.KeyVault/vaults", - "apiVersion": "2023-07-01", - "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[2]]", - "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[4]]", - "name": "[last(split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/'))]" + "containerSize": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Size of the function container." + } + }, + "dailyMemoryTimeQuota": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Maximum allowed daily memory-time quota (applicable on dynamic apps only)." + } + }, + "enabled": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Setting this value to false disables the app (takes the app offline)." + } }, - "avmTelemetry": { - "condition": "[parameters('enableTelemetry')]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2024-03-01", - "name": "[format('46d3xbcp.res.sql-server.{0}.{1}', replace('0.15.0', '.', '-'), 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" - } - } - } + "hostNameSslStates": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. Hostname SSL states are used to manage the SSL bindings for app's hostnames." } }, - "server": { - "type": "Microsoft.Sql/servers", - "apiVersion": "2023-08-01-preview", - "name": "[parameters('name')]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", - "identity": "[variables('identity')]", - "properties": { - "administratorLogin": "[if(not(empty(parameters('administratorLogin'))), parameters('administratorLogin'), null())]", - "administratorLoginPassword": "[if(not(empty(parameters('administratorLoginPassword'))), parameters('administratorLoginPassword'), null())]", - "administrators": "[union(createObject('administratorType', 'ActiveDirectory'), coalesce(parameters('administrators'), createObject()))]", - "federatedClientId": "[parameters('federatedClientId')]", - "isIPv6Enabled": "[parameters('isIPv6Enabled')]", - "keyId": "[if(not(equals(parameters('customerManagedKey'), null())), if(not(empty(tryGet(parameters('customerManagedKey'), 'keyVersion'))), format('{0}/{1}', reference('cMKKeyVault::cMKKey').keyUri, tryGet(parameters('customerManagedKey'), 'keyVersion')), if(coalesce(tryGet(parameters('customerManagedKey'), 'autoRotationEnabled'), true()), reference('cMKKeyVault::cMKKey').keyUri, reference('cMKKeyVault::cMKKey').keyUriWithVersion)), null())]", - "version": "12.0", - "minimalTlsVersion": "[parameters('minimalTlsVersion')]", - "primaryUserAssignedIdentityId": "[if(not(empty(parameters('primaryUserAssignedIdentityId'))), parameters('primaryUserAssignedIdentityId'), null())]", - "publicNetworkAccess": "[if(not(empty(parameters('publicNetworkAccess'))), parameters('publicNetworkAccess'), if(and(and(not(empty(parameters('privateEndpoints'))), empty(parameters('firewallRules'))), empty(parameters('virtualNetworkRules'))), 'Disabled', null()))]", - "restrictOutboundNetworkAccess": "[if(not(empty(parameters('restrictOutboundNetworkAccess'))), parameters('restrictOutboundNetworkAccess'), null())]" - }, - "dependsOn": [ - "cMKKeyVault::cMKKey" - ] + "hyperV": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Hyper-V sandbox." + } }, - "server_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.Sql/servers/{0}', parameters('name'))]", - "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", - "properties": { - "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", - "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" - }, - "dependsOn": [ - "server" - ] + "redundancyMode": { + "type": "string", + "defaultValue": "None", + "allowedValues": [ + "ActiveActive", + "Failover", + "GeoRedundant", + "Manual", + "None" + ], + "metadata": { + "description": "Optional. Site redundancy mode." + } }, - "server_roleAssignments": { - "copy": { - "name": "server_roleAssignments", - "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Sql/servers/{0}', parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Sql/servers', 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": [ - "server" - ] + "basicPublishingCredentialsPolicies": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The site publishing credential policy names which are associated with the sites." + } }, - "server_databases": { - "copy": { - "name": "server_databases", - "count": "[length(parameters('databases'))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-Sql-DB-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "serverName": { - "value": "[parameters('name')]" - }, - "location": { - "value": "[parameters('location')]" - }, - "tags": { - "value": "[coalesce(tryGet(parameters('databases')[copyIndex()], 'tags'), parameters('tags'))]" - }, - "name": { - "value": "[parameters('databases')[copyIndex()].name]" - }, - "sku": { - "value": "[tryGet(parameters('databases')[copyIndex()], 'sku')]" - }, - "autoPauseDelay": { - "value": "[tryGet(parameters('databases')[copyIndex()], 'autoPauseDelay')]" - }, - "availabilityZone": { - "value": "[tryGet(parameters('databases')[copyIndex()], 'availabilityZone')]" - }, - "catalogCollation": { - "value": "[tryGet(parameters('databases')[copyIndex()], 'catalogCollation')]" - }, - "collation": { - "value": "[tryGet(parameters('databases')[copyIndex()], 'collation')]" - }, - "createMode": { - "value": "[tryGet(parameters('databases')[copyIndex()], 'createMode')]" - }, - "elasticPoolResourceId": { - "value": "[tryGet(parameters('databases')[copyIndex()], 'elasticPoolResourceId')]" - }, - "encryptionProtector": { - "value": "[tryGet(parameters('databases')[copyIndex()], 'encryptionProtector')]" - }, - "encryptionProtectorAutoRotation": { - "value": "[tryGet(parameters('databases')[copyIndex()], 'encryptionProtectorAutoRotation')]" - }, - "federatedClientId": { - "value": "[tryGet(parameters('databases')[copyIndex()], 'federatedClientId')]" - }, - "freeLimitExhaustionBehavior": { - "value": "[tryGet(parameters('databases')[copyIndex()], 'freeLimitExhaustionBehavior')]" - }, - "highAvailabilityReplicaCount": { - "value": "[tryGet(parameters('databases')[copyIndex()], 'highAvailabilityReplicaCount')]" - }, - "isLedgerOn": { - "value": "[tryGet(parameters('databases')[copyIndex()], 'isLedgerOn')]" - }, - "licenseType": { - "value": "[tryGet(parameters('databases')[copyIndex()], 'licenseType')]" - }, - "longTermRetentionBackupResourceId": { - "value": "[tryGet(parameters('databases')[copyIndex()], 'longTermRetentionBackupResourceId')]" - }, - "maintenanceConfigurationId": { - "value": "[tryGet(parameters('databases')[copyIndex()], 'maintenanceConfigurationId')]" - }, - "manualCutover": { - "value": "[tryGet(parameters('databases')[copyIndex()], 'manualCutover')]" - }, - "maxSizeBytes": { - "value": "[tryGet(parameters('databases')[copyIndex()], 'maxSizeBytes')]" - }, - "minCapacity": { - "value": "[tryGet(parameters('databases')[copyIndex()], 'minCapacity')]" - }, - "performCutover": { - "value": "[tryGet(parameters('databases')[copyIndex()], 'performCutover')]" - }, - "preferredEnclaveType": { - "value": "[tryGet(parameters('databases')[copyIndex()], 'preferredEnclaveType')]" - }, - "readScale": { - "value": "[tryGet(parameters('databases')[copyIndex()], 'readScale')]" - }, - "recoverableDatabaseResourceId": { - "value": "[tryGet(parameters('databases')[copyIndex()], 'recoverableDatabaseResourceId')]" - }, - "recoveryServicesRecoveryPointResourceId": { - "value": "[tryGet(parameters('databases')[copyIndex()], 'recoveryServicesRecoveryPointResourceId')]" - }, - "requestedBackupStorageRedundancy": { - "value": "[tryGet(parameters('databases')[copyIndex()], 'requestedBackupStorageRedundancy')]" - }, - "restorableDroppedDatabaseResourceId": { - "value": "[tryGet(parameters('databases')[copyIndex()], 'restorableDroppedDatabaseResourceId')]" - }, - "restorePointInTime": { - "value": "[tryGet(parameters('databases')[copyIndex()], 'restorePointInTime')]" - }, - "sampleName": { - "value": "[tryGet(parameters('databases')[copyIndex()], 'sampleName')]" - }, - "secondaryType": { - "value": "[tryGet(parameters('databases')[copyIndex()], 'secondaryType')]" - }, - "sourceDatabaseDeletionDate": { - "value": "[tryGet(parameters('databases')[copyIndex()], 'sourceDatabaseDeletionDate')]" - }, - "sourceDatabaseResourceId": { - "value": "[tryGet(parameters('databases')[copyIndex()], 'sourceDatabaseResourceId')]" - }, - "sourceResourceId": { - "value": "[tryGet(parameters('databases')[copyIndex()], 'sourceResourceId')]" - }, - "useFreeLimit": { - "value": "[tryGet(parameters('databases')[copyIndex()], 'useFreeLimit')]" - }, - "zoneRedundant": { - "value": "[tryGet(parameters('databases')[copyIndex()], 'zoneRedundant')]" - }, - "diagnosticSettings": { - "value": "[tryGet(parameters('databases')[copyIndex()], 'diagnosticSettings')]" - }, - "backupShortTermRetentionPolicy": { - "value": "[tryGet(parameters('databases')[copyIndex()], 'backupShortTermRetentionPolicy')]" - }, - "backupLongTermRetentionPolicy": { - "value": "[tryGet(parameters('databases')[copyIndex()], 'backupLongTermRetentionPolicy')]" - } - }, - "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.34.44.8038", - "templateHash": "10036542469147733417" - }, - "name": "SQL Server Database", - "description": "This module deploys an Azure SQL Server Database." - }, - "definitions": { - "databaseSkuType": { - "type": "object", - "properties": { - "capacity": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The capacity of the particular SKU." - } - }, - "family": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. If the service has different generations of hardware, for the same SKU, then that can be captured here." - } - }, - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the SKU, typically, a letter + Number code, e.g. P3." - } - }, - "size": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Size of the particular SKU." - } - }, - "tier": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The tier or edition of the particular SKU, e.g. Basic, Premium." - } - } - }, - "metadata": { - "__bicep_export!": true, - "description": "The database SKU." - } - }, - "shortTermBackupRetentionPolicyType": { - "type": "object", - "properties": { - "diffBackupIntervalInHours": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. Differential backup interval in hours. For Hyperscale tiers this value will be ignored." - } - }, - "retentionDays": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. Point-in-time retention in days." - } - } - }, - "metadata": { - "__bicep_export!": true, - "description": "The short-term backup retention policy for the database." - } - }, - "longTermBackupRetentionPolicyType": { - "type": "object", - "properties": { - "backupStorageAccessTier": { - "type": "string", - "allowedValues": [ - "Archive", - "Hot" - ], - "nullable": true, - "metadata": { - "description": "Optional. The BackupStorageAccessTier for the LTR backups." - } - }, - "makeBackupsImmutable": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. The setting whether to make LTR backups immutable." - } - }, - "monthlyRetention": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Monthly retention in ISO 8601 duration format." - } - }, - "weeklyRetention": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Weekly retention in ISO 8601 duration format." - } - }, - "weekOfYear": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. Week of year backup to keep for yearly retention." - } - }, - "yearlyRetention": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Yearly retention in ISO 8601 duration format." - } - } - }, - "metadata": { - "__bicep_export!": true, - "description": "The long-term backup retention policy for the database." - } - }, - "diagnosticSettingFullType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the diagnostic setting." - } - }, - "logCategoriesAndGroups": { - "type": "array", - "items": { - "type": "object", - "properties": { - "category": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." - } - }, - "categoryGroup": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." - } - }, - "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 logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." - } - }, - "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 both logs & metrics are supported by the resource provider.", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } - } + "hybridConnectionRelays": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. Names of hybrid connection relays to connect app with." + } + }, + "publicNetworkAccess": { + "type": "string", + "nullable": true, + "allowedValues": [ + "Enabled", + "Disabled" + ], + "metadata": { + "description": "Optional. Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set." + } + }, + "e2eEncryptionEnabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. End to End Encryption Setting." + } + } + }, + "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)))))]" + } + ], + "enableReferencedModulesTelemetry": false, + "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", + "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned, UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', 'None')), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", + "builtInRoleNames": { + "App Compliance Automation Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f37683f-2463-46b6-9ce7-9b788b988ba2')]", + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]", + "Web Plan Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2cc479cb-7b4d-49a8-b449-8c00fd0f0a4b')]", + "Website Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'de139f84-1756-47ae-9be6-808fbbe84772')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.web-site.{0}.{1}', replace('0.15.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" + } + } + } + } + }, + "app": { + "type": "Microsoft.Web/sites", + "apiVersion": "2024-04-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "kind": "[parameters('kind')]", + "tags": "[parameters('tags')]", + "identity": "[variables('identity')]", + "properties": { + "managedEnvironmentId": "[if(not(empty(parameters('managedEnvironmentId'))), parameters('managedEnvironmentId'), null())]", + "serverFarmId": "[parameters('serverFarmResourceId')]", + "clientAffinityEnabled": "[parameters('clientAffinityEnabled')]", + "httpsOnly": "[parameters('httpsOnly')]", + "hostingEnvironmentProfile": "[if(not(empty(parameters('appServiceEnvironmentResourceId'))), createObject('id', parameters('appServiceEnvironmentResourceId')), null())]", + "storageAccountRequired": "[parameters('storageAccountRequired')]", + "keyVaultReferenceIdentity": "[parameters('keyVaultAccessIdentityResourceId')]", + "virtualNetworkSubnetId": "[parameters('virtualNetworkSubnetId')]", + "siteConfig": "[parameters('siteConfig')]", + "functionAppConfig": "[parameters('functionAppConfig')]", + "clientCertEnabled": "[parameters('clientCertEnabled')]", + "clientCertExclusionPaths": "[parameters('clientCertExclusionPaths')]", + "clientCertMode": "[parameters('clientCertMode')]", + "cloningInfo": "[parameters('cloningInfo')]", + "containerSize": "[parameters('containerSize')]", + "dailyMemoryTimeQuota": "[parameters('dailyMemoryTimeQuota')]", + "enabled": "[parameters('enabled')]", + "hostNameSslStates": "[parameters('hostNameSslStates')]", + "hyperV": "[parameters('hyperV')]", + "redundancyMode": "[parameters('redundancyMode')]", + "publicNetworkAccess": "[if(not(empty(parameters('publicNetworkAccess'))), parameters('publicNetworkAccess'), if(not(empty(parameters('privateEndpoints'))), 'Disabled', 'Enabled'))]", + "vnetContentShareEnabled": "[parameters('vnetContentShareEnabled')]", + "vnetImagePullEnabled": "[parameters('vnetImagePullEnabled')]", + "vnetRouteAllEnabled": "[parameters('vnetRouteAllEnabled')]", + "scmSiteAlsoStopped": "[parameters('scmSiteAlsoStopped')]", + "endToEndEncryptionEnabled": "[parameters('e2eEncryptionEnabled')]" + } + }, + "app_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.Web/sites/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "app" + ] + }, + "app_diagnosticSettings": { + "copy": { + "name": "app_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Web/sites/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null } }, - "parameters": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the database." - } - }, - "serverName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent SQL Server. Required if the template is used in a standalone deployment." - } - }, - "sku": { - "$ref": "#/definitions/databaseSkuType", - "defaultValue": { - "name": "GP_Gen5_2", - "tier": "GeneralPurpose" - }, - "metadata": { - "description": "Optional. The database SKU." - } - }, - "autoPauseDelay": { - "type": "int", - "defaultValue": -1, - "metadata": { - "description": "Optional. Time in minutes after which database is automatically paused. A value of -1 means that automatic pause is disabled." - } - }, - "availabilityZone": { - "type": "string", - "allowedValues": [ - "1", - "2", - "3", - "NoPreference" - ], - "defaultValue": "NoPreference", - "metadata": { - "description": "Optional. Specifies the availability zone the database is pinned to." - } - }, - "catalogCollation": { - "type": "string", - "defaultValue": "DATABASE_DEFAULT", - "metadata": { - "description": "Optional. Collation of the metadata catalog." - } - }, - "collation": { - "type": "string", - "defaultValue": "SQL_Latin1_General_CP1_CI_AS", - "metadata": { - "description": "Optional. The collation of the database." - } - }, - "createMode": { - "type": "string", - "allowedValues": [ - "Copy", - "Default", - "OnlineSecondary", - "PointInTimeRestore", - "Recovery", - "Restore", - "RestoreExternalBackup", - "RestoreExternalBackupSecondary", - "RestoreLongTermRetentionBackup", - "Secondary" - ], - "defaultValue": "Default", - "metadata": { - "description": "Optional. Specifies the mode of database creation." - } - }, - "elasticPoolResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The resource ID of the elastic pool containing this database." - } - }, - "encryptionProtector": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The azure key vault URI of the database if it's configured with per Database Customer Managed Keys." - } - }, - "encryptionProtectorAutoRotation": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. The flag to enable or disable auto rotation of database encryption protector AKV key." - } - }, - "federatedClientId": { - "type": "string", - "nullable": true, - "minLength": 36, - "maxLength": 36, - "metadata": { - "description": "Optional. The Client id used for cross tenant per database CMK scenario." - } - }, - "freeLimitExhaustionBehavior": { - "type": "string", - "allowedValues": [ - "AutoPause", - "BillOverUsage" - ], - "nullable": true, - "metadata": { - "description": "Optional. Specifies the behavior when monthly free limits are exhausted for the free database." - } - }, - "highAvailabilityReplicaCount": { - "type": "int", - "defaultValue": 0, - "metadata": { - "description": "Optional. The number of readonly secondary replicas associated with the database." - } - }, - "isLedgerOn": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "Optional. Whether or not this database is a ledger database, which means all tables in the database are ledger tables. Note: the value of this property cannot be changed after the database has been created." - } - }, - "licenseType": { - "type": "string", - "allowedValues": [ - "BasePrice", - "LicenseIncluded" - ], - "nullable": true, - "metadata": { - "description": "Optional. The license type to apply for this database." - } - }, - "longTermRetentionBackupResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The resource identifier of the long term retention backup associated with create operation of this database." - } - }, - "maintenanceConfigurationId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Maintenance configuration ID assigned to the database. This configuration defines the period when the maintenance updates will occur." - } - }, - "manualCutover": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Whether or not customer controlled manual cutover needs to be done during Update Database operation to Hyperscale tier." - } - }, - "maxSizeBytes": { - "type": "int", - "defaultValue": 34359738368, - "metadata": { - "description": "Optional. The max size of the database expressed in bytes." - } - }, - "minCapacity": { - "type": "string", - "defaultValue": "0", - "metadata": { - "description": "Optional. Minimal capacity that database will always have allocated." - } - }, - "performCutover": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. To trigger customer controlled manual cutover during the wait state while Scaling operation is in progress." - } - }, - "preferredEnclaveType": { - "type": "string", - "allowedValues": [ - "Default", - "VBS" - ], - "nullable": true, - "metadata": { - "description": "Optional. Type of enclave requested on the database i.e. Default or VBS enclaves." - } - }, - "readScale": { - "type": "string", - "allowedValues": [ - "Disabled", - "Enabled" - ], - "defaultValue": "Disabled", - "metadata": { - "description": "Optional. The state of read-only routing." - } - }, - "recoverableDatabaseResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The resource identifier of the recoverable database associated with create operation of this database." - } - }, - "recoveryServicesRecoveryPointResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The resource identifier of the recovery point associated with create operation of this database." - } - }, - "requestedBackupStorageRedundancy": { - "type": "string", - "allowedValues": [ - "Geo", - "GeoZone", - "Local", - "Zone" - ], - "defaultValue": "Local", - "metadata": { - "description": "Optional. The storage account type to be used to store backups for this database." - } - }, - "restorableDroppedDatabaseResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The resource identifier of the restorable dropped database associated with create operation of this database." - } - }, - "restorePointInTime": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Point in time (ISO8601 format) of the source database to restore when createMode set to Restore or PointInTimeRestore." - } + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "app" + ] + }, + "app_roleAssignments": { + "copy": { + "name": "app_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Web/sites/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Web/sites', 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": [ + "app" + ] + }, + "app_appsettings": { + "condition": "[or(or(not(empty(parameters('appSettingsKeyValuePairs'))), not(empty(parameters('appInsightResourceId')))), not(empty(parameters('storageAccountResourceId'))))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Site-Config-AppSettings', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "appName": { + "value": "[parameters('name')]" + }, + "kind": { + "value": "[parameters('kind')]" + }, + "storageAccountResourceId": { + "value": "[parameters('storageAccountResourceId')]" + }, + "storageAccountUseIdentityAuthentication": { + "value": "[parameters('storageAccountUseIdentityAuthentication')]" + }, + "appInsightResourceId": { + "value": "[parameters('appInsightResourceId')]" + }, + "appSettingsKeyValuePairs": { + "value": "[parameters('appSettingsKeyValuePairs')]" + }, + "currentAppSettings": "[if(not(empty(resourceId('Microsoft.Web/sites', parameters('name')))), createObject('value', list(format('{0}/config/appsettings', resourceId('Microsoft.Web/sites', parameters('name'))), '2023-12-01').properties), createObject('value', createObject()))]" + }, + "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.33.93.31351", + "templateHash": "12262977018813780856" }, - "sampleName": { + "name": "Site App Settings", + "description": "This module deploys a Site App Setting." + }, + "parameters": { + "appName": { "type": "string", - "defaultValue": "", "metadata": { - "description": "Optional. The name of the sample schema to apply when creating this database." + "description": "Conditional. The name of the parent site resource. Required if the template is used in a standalone deployment." } }, - "secondaryType": { + "kind": { "type": "string", "allowedValues": [ - "Geo", - "Named", - "Standby" + "functionapp", + "functionapp,linux", + "functionapp,workflowapp", + "functionapp,workflowapp,linux", + "functionapp,linux,container", + "functionapp,linux,container,azurecontainerapps", + "app,linux", + "app", + "linux,api", + "api", + "app,linux,container", + "app,container,windows" ], - "nullable": true, - "metadata": { - "description": "Optional. The secondary type of the database if it is a secondary." - } - }, - "sourceDatabaseDeletionDate": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The time that the database was deleted when restoring a deleted database." - } - }, - "sourceDatabaseResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The resource identifier of the source database associated with create operation of this database." - } - }, - "sourceResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The resource identifier of the source associated with the create operation of this database." - } - }, - "useFreeLimit": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Whether or not the database uses free monthly limits. Allowed on one database in a subscription." - } - }, - "zoneRedundant": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Optional. Whether or not this database is zone redundant." - } - }, - "tags": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Tags of the resource." - } - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]", - "metadata": { - "description": "Optional. Location for all resources." - } - }, - "diagnosticSettings": { - "type": "array", - "items": { - "$ref": "#/definitions/diagnosticSettingFullType" - }, - "nullable": true, - "metadata": { - "description": "Optional. The diagnostic settings of the service." - } - }, - "backupShortTermRetentionPolicy": { - "$ref": "#/definitions/shortTermBackupRetentionPolicyType", - "nullable": true, - "metadata": { - "description": "Optional. The short term backup retention policy to create for the database." - } - }, - "backupLongTermRetentionPolicy": { - "$ref": "#/definitions/longTermBackupRetentionPolicyType", - "nullable": true, "metadata": { - "description": "Optional. The long term backup retention policy to create for the database." + "description": "Required. Type of site to deploy." } - } - }, - "resources": { - "server": { - "existing": true, - "type": "Microsoft.Sql/servers", - "apiVersion": "2023-08-01-preview", - "name": "[parameters('serverName')]" }, - "database": { - "type": "Microsoft.Sql/servers/databases", - "apiVersion": "2023-08-01-preview", - "name": "[format('{0}/{1}', parameters('serverName'), parameters('name'))]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", - "sku": "[parameters('sku')]", - "properties": { - "autoPauseDelay": "[parameters('autoPauseDelay')]", - "availabilityZone": "[parameters('availabilityZone')]", - "catalogCollation": "[parameters('catalogCollation')]", - "collation": "[parameters('collation')]", - "createMode": "[parameters('createMode')]", - "elasticPoolId": "[parameters('elasticPoolResourceId')]", - "encryptionProtector": "[parameters('encryptionProtector')]", - "encryptionProtectorAutoRotation": "[parameters('encryptionProtectorAutoRotation')]", - "federatedClientId": "[parameters('federatedClientId')]", - "freeLimitExhaustionBehavior": "[parameters('freeLimitExhaustionBehavior')]", - "highAvailabilityReplicaCount": "[parameters('highAvailabilityReplicaCount')]", - "isLedgerOn": "[parameters('isLedgerOn')]", - "licenseType": "[parameters('licenseType')]", - "longTermRetentionBackupResourceId": "[parameters('longTermRetentionBackupResourceId')]", - "maintenanceConfigurationId": "[parameters('maintenanceConfigurationId')]", - "manualCutover": "[parameters('manualCutover')]", - "maxSizeBytes": "[parameters('maxSizeBytes')]", - "minCapacity": "[if(not(empty(parameters('minCapacity'))), json(parameters('minCapacity')), 0)]", - "performCutover": "[parameters('performCutover')]", - "preferredEnclaveType": "[parameters('preferredEnclaveType')]", - "readScale": "[parameters('readScale')]", - "recoverableDatabaseId": "[parameters('recoverableDatabaseResourceId')]", - "recoveryServicesRecoveryPointId": "[parameters('recoveryServicesRecoveryPointResourceId')]", - "requestedBackupStorageRedundancy": "[parameters('requestedBackupStorageRedundancy')]", - "restorableDroppedDatabaseId": "[parameters('restorableDroppedDatabaseResourceId')]", - "restorePointInTime": "[parameters('restorePointInTime')]", - "sampleName": "[parameters('sampleName')]", - "secondaryType": "[parameters('secondaryType')]", - "sourceDatabaseDeletionDate": "[parameters('sourceDatabaseDeletionDate')]", - "sourceDatabaseId": "[parameters('sourceDatabaseResourceId')]", - "sourceResourceId": "[parameters('sourceResourceId')]", - "useFreeLimit": "[parameters('useFreeLimit')]", - "zoneRedundant": "[parameters('zoneRedundant')]" + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Required if app of kind functionapp. Resource ID of the storage account to manage triggers and logging function executions." } }, - "database_diagnosticSettings": { - "copy": { - "name": "database_diagnosticSettings", - "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" - }, - "type": "Microsoft.Insights/diagnosticSettings", - "apiVersion": "2021-05-01-preview", - "scope": "[format('Microsoft.Sql/servers/{0}/databases/{1}', parameters('serverName'), parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", - "properties": { - "copy": [ - { - "name": "metrics", - "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", - "input": { - "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", - "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", - "timeGrain": null - } - }, - { - "name": "logs", - "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", - "input": { - "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", - "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", - "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" - } - } - ], - "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", - "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", - "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", - "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", - "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", - "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" - }, - "dependsOn": [ - "database" - ] + "storageAccountUseIdentityAuthentication": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. If the provided storage account requires Identity based authentication ('allowSharedKeyAccess' is set to false). When set to true, the minimum role assignment required for the App Service Managed Identity to the storage account is 'Storage Blob Data Owner'." + } }, - "database_backupShortTermRetentionPolicy": { - "condition": "[not(empty(parameters('backupShortTermRetentionPolicy')))]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-{1}-shBakRetPol', uniqueString(deployment().name, parameters('location')), parameters('name'))]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "serverName": { - "value": "[parameters('serverName')]" - }, - "databaseName": { - "value": "[parameters('name')]" - }, - "diffBackupIntervalInHours": { - "value": "[tryGet(parameters('backupShortTermRetentionPolicy'), 'diffBackupIntervalInHours')]" - }, - "retentionDays": { - "value": "[tryGet(parameters('backupShortTermRetentionPolicy'), 'retentionDays')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.34.44.8038", - "templateHash": "586417749840962852" - }, - "name": "Azure SQL Server Database Short Term Backup Retention Policies", - "description": "This module deploys an Azure SQL Server Database Short-Term Backup Retention Policy." - }, - "parameters": { - "serverName": { - "type": "string", - "metadata": { - "description": "Required. The name of the parent SQL Server." - } - }, - "databaseName": { - "type": "string", - "metadata": { - "description": "Required. The name of the parent database." - } - }, - "diffBackupIntervalInHours": { - "type": "int", - "defaultValue": 24, - "metadata": { - "description": "Optional. Differential backup interval in hours. For Hyperscal tiers this value will be ignored." - } - }, - "retentionDays": { - "type": "int", - "defaultValue": 7, - "metadata": { - "description": "Optional. Poin-in-time retention in days." - } - } - }, - "resources": [ - { - "type": "Microsoft.Sql/servers/databases/backupShortTermRetentionPolicies", - "apiVersion": "2023-08-01-preview", - "name": "[format('{0}/{1}/{2}', parameters('serverName'), parameters('databaseName'), 'default')]", - "properties": { - "diffBackupIntervalInHours": "[if(equals(reference(resourceId('Microsoft.Sql/servers/databases', parameters('serverName'), parameters('databaseName')), '2023-08-01-preview', 'full').sku.tier, 'Hyperscale'), null(), parameters('diffBackupIntervalInHours'))]", - "retentionDays": "[parameters('retentionDays')]" - } - } - ], - "outputs": { - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group the short-term policy was deployed into." - }, - "value": "[resourceGroup().name]" - }, - "name": { - "type": "string", - "metadata": { - "description": "The name of the short-term policy." - }, - "value": "default" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the short-term policy." - }, - "value": "[resourceId('Microsoft.Sql/servers/databases/backupShortTermRetentionPolicies', parameters('serverName'), parameters('databaseName'), 'default')]" - } - } - } - }, - "dependsOn": [ - "database" - ] + "appInsightResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the app insight to leverage for this resource." + } }, - "database_backupLongTermRetentionPolicy": { - "condition": "[not(empty(parameters('backupLongTermRetentionPolicy')))]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-{1}-lgBakRetPol', uniqueString(deployment().name, parameters('location')), parameters('name'))]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "serverName": { - "value": "[parameters('serverName')]" - }, - "databaseName": { - "value": "[parameters('name')]" - }, - "backupStorageAccessTier": { - "value": "[tryGet(parameters('backupLongTermRetentionPolicy'), 'backupStorageAccessTier')]" - }, - "makeBackupsImmutable": { - "value": "[tryGet(parameters('backupLongTermRetentionPolicy'), 'makeBackupsImmutable')]" - }, - "weeklyRetention": { - "value": "[tryGet(parameters('backupLongTermRetentionPolicy'), 'weeklyRetention')]" - }, - "monthlyRetention": { - "value": "[tryGet(parameters('backupLongTermRetentionPolicy'), 'monthlyRetention')]" - }, - "yearlyRetention": { - "value": "[tryGet(parameters('backupLongTermRetentionPolicy'), 'yearlyRetention')]" - }, - "weekOfYear": { - "value": "[tryGet(parameters('backupLongTermRetentionPolicy'), 'weekOfYear')]" - } - }, - "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.34.44.8038", - "templateHash": "15078686386861189890" - }, - "name": "SQL Server Database Long Term Backup Retention Policies", - "description": "This module deploys an Azure SQL Server Database Long-Term Backup Retention Policy." - }, - "parameters": { - "serverName": { - "type": "string", - "metadata": { - "description": "Required. The name of the parent SQL Server." - } - }, - "databaseName": { - "type": "string", - "metadata": { - "description": "Required. The name of the parent database." - } - }, - "backupStorageAccessTier": { - "type": "string", - "allowedValues": [ - "Archive", - "Hot" - ], - "nullable": true, - "metadata": { - "description": "Optional. The BackupStorageAccessTier for the LTR backups." - } - }, - "makeBackupsImmutable": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. The setting whether to make LTR backups immutable." - } - }, - "monthlyRetention": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Monthly retention in ISO 8601 duration format." - } - }, - "weeklyRetention": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Weekly retention in ISO 8601 duration format." - } - }, - "weekOfYear": { - "type": "int", - "defaultValue": 1, - "metadata": { - "description": "Optional. Week of year backup to keep for yearly retention." - } - }, - "yearlyRetention": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Yearly retention in ISO 8601 duration format." - } - } - }, - "resources": { - "server::database": { - "existing": true, - "type": "Microsoft.Sql/servers/databases", - "apiVersion": "2023-08-01-preview", - "name": "[format('{0}/{1}', parameters('serverName'), parameters('databaseName'))]" - }, - "server": { - "existing": true, - "type": "Microsoft.Sql/servers", - "apiVersion": "2023-08-01-preview", - "name": "[parameters('serverName')]" - }, - "backupLongTermRetentionPolicy": { - "type": "Microsoft.Sql/servers/databases/backupLongTermRetentionPolicies", - "apiVersion": "2023-05-01-preview", - "name": "[format('{0}/{1}/{2}', parameters('serverName'), parameters('databaseName'), 'default')]", - "properties": { - "backupStorageAccessTier": "[parameters('backupStorageAccessTier')]", - "makeBackupsImmutable": "[parameters('makeBackupsImmutable')]", - "monthlyRetention": "[parameters('monthlyRetention')]", - "weeklyRetention": "[parameters('weeklyRetention')]", - "weekOfYear": "[parameters('weekOfYear')]", - "yearlyRetention": "[parameters('yearlyRetention')]" - } - } - }, - "outputs": { - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group the long-term policy was deployed into." - }, - "value": "[resourceGroup().name]" - }, - "name": { - "type": "string", - "metadata": { - "description": "The name of the long-term policy." - }, - "value": "default" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the long-term policy." - }, - "value": "[resourceId('Microsoft.Sql/servers/databases/backupLongTermRetentionPolicies', parameters('serverName'), parameters('databaseName'), 'default')]" - } - } - } - }, + "appSettingsKeyValuePairs": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The app settings key-value pairs except for AzureWebJobsStorage, AzureWebJobsDashboard, APPINSIGHTS_INSTRUMENTATIONKEY and APPLICATIONINSIGHTS_CONNECTION_STRING." + } + }, + "currentAppSettings": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. The current app settings." + } + } + }, + "resources": { + "app": { + "existing": true, + "type": "Microsoft.Web/sites", + "apiVersion": "2023-12-01", + "name": "[parameters('appName')]" + }, + "appInsight": { + "condition": "[not(empty(parameters('appInsightResourceId')))]", + "existing": true, + "type": "Microsoft.Insights/components", + "apiVersion": "2020-02-02", + "subscriptionId": "[split(parameters('appInsightResourceId'), '/')[2]]", + "resourceGroup": "[split(parameters('appInsightResourceId'), '/')[4]]", + "name": "[last(split(parameters('appInsightResourceId'), '/'))]" + }, + "storageAccount": { + "condition": "[not(empty(parameters('storageAccountResourceId')))]", + "existing": true, + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2023-05-01", + "subscriptionId": "[split(parameters('storageAccountResourceId'), '/')[2]]", + "resourceGroup": "[split(parameters('storageAccountResourceId'), '/')[4]]", + "name": "[last(split(parameters('storageAccountResourceId'), '/'))]" + }, + "appSettings": { + "type": "Microsoft.Web/sites/config", + "apiVersion": "2024-04-01", + "name": "[format('{0}/{1}', parameters('appName'), 'appsettings')]", + "kind": "[parameters('kind')]", + "properties": "[union(coalesce(parameters('currentAppSettings'), createObject()), coalesce(parameters('appSettingsKeyValuePairs'), createObject()), if(and(not(empty(parameters('storageAccountResourceId'))), not(parameters('storageAccountUseIdentityAuthentication'))), createObject('AzureWebJobsStorage', format('DefaultEndpointsProtocol=https;AccountName={0};AccountKey={1};EndpointSuffix={2}', last(split(parameters('storageAccountResourceId'), '/')), listKeys(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('storageAccountResourceId'), '/')[2], split(parameters('storageAccountResourceId'), '/')[4]), 'Microsoft.Storage/storageAccounts', last(split(parameters('storageAccountResourceId'), '/'))), '2023-05-01').keys[0].value, environment().suffixes.storage)), if(and(not(empty(parameters('storageAccountResourceId'))), parameters('storageAccountUseIdentityAuthentication')), union(createObject('AzureWebJobsStorage__accountName', last(split(parameters('storageAccountResourceId'), '/'))), createObject('AzureWebJobsStorage__blobServiceUri', reference('storageAccount').primaryEndpoints.blob), createObject('AzureWebJobsStorage__queueServiceUri', reference('storageAccount').primaryEndpoints.queue), createObject('AzureWebJobsStorage__tableServiceUri', reference('storageAccount').primaryEndpoints.table)), createObject())), if(not(empty(parameters('appInsightResourceId'))), createObject('APPLICATIONINSIGHTS_CONNECTION_STRING', reference('appInsight').ConnectionString), createObject()))]", "dependsOn": [ - "database" + "appInsight", + "storageAccount" ] } }, @@ -84253,400 +94919,417 @@ "name": { "type": "string", "metadata": { - "description": "The name of the deployed database." + "description": "The name of the site config." }, - "value": "[parameters('name')]" + "value": "appsettings" }, "resourceId": { "type": "string", "metadata": { - "description": "The resource ID of the deployed database." + "description": "The resource ID of the site config." }, - "value": "[resourceId('Microsoft.Sql/servers/databases', parameters('serverName'), parameters('name'))]" + "value": "[resourceId('Microsoft.Web/sites/config', parameters('appName'), 'appsettings')]" }, "resourceGroupName": { "type": "string", "metadata": { - "description": "The resource group of the deployed database." + "description": "The resource group the site config was deployed into." }, "value": "[resourceGroup().name]" - }, - "location": { - "type": "string", - "metadata": { - "description": "The location the resource was deployed into." - }, - "value": "[reference('database', '2023-08-01-preview', 'full').location]" } } } }, "dependsOn": [ - "server", - "server_elasticPools" + "app" ] }, - "server_elasticPools": { - "copy": { - "name": "server_elasticPools", - "count": "[length(parameters('elasticPools'))]" - }, + "app_authsettingsv2": { + "condition": "[not(empty(parameters('authSettingV2Configuration')))]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-SQLServer-ElasticPool-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "name": "[format('{0}-Site-Config-AuthSettingsV2', uniqueString(deployment().name, parameters('location')))]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "serverName": { + "appName": { "value": "[parameters('name')]" }, - "location": { - "value": "[parameters('location')]" - }, - "tags": { - "value": "[coalesce(tryGet(parameters('elasticPools')[copyIndex()], 'tags'), parameters('tags'))]" - }, - "name": { - "value": "[parameters('elasticPools')[copyIndex()].name]" - }, - "sku": { - "value": "[tryGet(parameters('elasticPools')[copyIndex()], 'sku')]" - }, - "autoPauseDelay": { - "value": "[tryGet(parameters('elasticPools')[copyIndex()], 'autoPauseDelay')]" - }, - "availabilityZone": { - "value": "[tryGet(parameters('elasticPools')[copyIndex()], 'availabilityZone')]" - }, - "highAvailabilityReplicaCount": { - "value": "[tryGet(parameters('elasticPools')[copyIndex()], 'highAvailabilityReplicaCount')]" - }, - "licenseType": { - "value": "[tryGet(parameters('elasticPools')[copyIndex()], 'licenseType')]" - }, - "maintenanceConfigurationId": { - "value": "[tryGet(parameters('elasticPools')[copyIndex()], 'maintenanceConfigurationId')]" - }, - "maxSizeBytes": { - "value": "[tryGet(parameters('elasticPools')[copyIndex()], 'maxSizeBytes')]" - }, - "minCapacity": { - "value": "[tryGet(parameters('elasticPools')[copyIndex()], 'minCapacity')]" - }, - "perDatabaseSettings": { - "value": "[tryGet(parameters('elasticPools')[copyIndex()], 'perDatabaseSettings')]" - }, - "preferredEnclaveType": { - "value": "[tryGet(parameters('elasticPools')[copyIndex()], 'preferredEnclaveType')]" + "kind": { + "value": "[parameters('kind')]" }, - "zoneRedundant": { - "value": "[tryGet(parameters('elasticPools')[copyIndex()], 'zoneRedundant')]" + "authSettingV2Configuration": { + "value": "[coalesce(parameters('authSettingV2Configuration'), createObject())]" } }, "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.34.44.8038", - "templateHash": "15648031842218670048" + "version": "0.33.93.31351", + "templateHash": "1129994114817101549" }, - "name": "SQL Server Elastic Pool", - "description": "This module deploys an Azure SQL Server Elastic Pool." + "name": "Site Auth Settings V2 Config", + "description": "This module deploys a Site Auth Settings V2 Configuration." }, - "definitions": { - "elasticPoolPerDatabaseSettingsType": { - "type": "object", - "properties": { - "autoPauseDelay": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. Auto Pause Delay for per database within pool." - } - }, - "maxCapacity": { - "type": "string", - "metadata": { - "description": "Required. The maximum capacity any one database can consume. Examples: '0.5', '2'." - } - }, - "minCapacity": { - "type": "string", - "metadata": { - "description": "Required. The minimum capacity all databases are guaranteed. Examples: '0.5', '1'." - } - } - }, + "parameters": { + "appName": { + "type": "string", "metadata": { - "__bicep_export!": true, - "description": "The per database settings for the elastic pool." + "description": "Conditional. The name of the parent site resource. Required if the template is used in a standalone deployment." } }, - "elasticPoolSkuType": { + "kind": { + "type": "string", + "allowedValues": [ + "functionapp", + "functionapp,linux", + "functionapp,workflowapp", + "functionapp,workflowapp,linux", + "functionapp,linux,container", + "functionapp,linux,container,azurecontainerapps", + "app,linux", + "app", + "linux,api", + "api", + "app,linux,container", + "app,container,windows" + ], + "metadata": { + "description": "Required. Type of site to deploy." + } + }, + "authSettingV2Configuration": { "type": "object", - "properties": { - "capacity": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. The capacity of the particular SKU." - } - }, - "family": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. If the service has different generations of hardware, for the same SKU, then that can be captured here." - } - }, - "name": { - "type": "string", - "allowedValues": [ - "BC_DC", - "BC_Gen5", - "BasicPool", - "GP_DC", - "GP_FSv2", - "GP_Gen5", - "HS_Gen5", - "HS_MOPRMS", - "HS_PRMS", - "PremiumPool", - "ServerlessPool", - "StandardPool" - ], - "metadata": { - "description": "Required. The name of the SKU, typically, a letter + Number code, e.g. P3." - } - }, - "size": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Size of the particular SKU." - } - }, - "tier": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The tier or edition of the particular SKU, e.g. Basic, Premium." - } - } + "metadata": { + "description": "Required. The auth settings V2 configuration." + } + } + }, + "resources": [ + { + "type": "Microsoft.Web/sites/config", + "apiVersion": "2024-04-01", + "name": "[format('{0}/{1}', parameters('appName'), 'authsettingsV2')]", + "kind": "[parameters('kind')]", + "properties": "[parameters('authSettingV2Configuration')]" + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the site config." + }, + "value": "authsettingsV2" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the site config." }, + "value": "[resourceId('Microsoft.Web/sites/config', parameters('appName'), 'authsettingsV2')]" + }, + "resourceGroupName": { + "type": "string", "metadata": { - "__bicep_export!": true, - "description": "The elastic pool SKU." - } + "description": "The resource group the site config was deployed into." + }, + "value": "[resourceGroup().name]" } + } + } + }, + "dependsOn": [ + "app" + ] + }, + "app_logssettings": { + "condition": "[not(empty(coalesce(parameters('logsConfiguration'), createObject())))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Site-Config-Logs', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "appName": { + "value": "[parameters('name')]" }, - "parameters": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the Elastic Pool." - } + "logsConfiguration": { + "value": "[parameters('logsConfiguration')]" + } + }, + "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.33.93.31351", + "templateHash": "17967336872376441757" }, - "serverName": { + "name": "Site logs Config", + "description": "This module deploys a Site logs Configuration." + }, + "parameters": { + "appName": { "type": "string", "metadata": { - "description": "Conditional. The name of the parent SQL Server. Required if the template is used in a standalone deployment." + "description": "Required. The name of the parent site resource." } }, - "tags": { + "logsConfiguration": { "type": "object", "nullable": true, "metadata": { - "description": "Optional. Tags of the resource." + "description": "Optional. The logs settings configuration." } + } + }, + "resources": { + "app": { + "existing": true, + "type": "Microsoft.Web/sites", + "apiVersion": "2024-04-01", + "name": "[parameters('appName')]" }, - "location": { + "webSettings": { + "type": "Microsoft.Web/sites/config", + "apiVersion": "2024-04-01", + "name": "[format('{0}/{1}', parameters('appName'), 'logs')]", + "kind": "string", + "properties": "[parameters('logsConfiguration')]" + } + }, + "outputs": { + "name": { "type": "string", - "defaultValue": "[resourceGroup().location]", "metadata": { - "description": "Optional. Location for all resources." - } - }, - "sku": { - "$ref": "#/definitions/elasticPoolSkuType", - "defaultValue": { - "capacity": 2, - "name": "GP_Gen5", - "tier": "GeneralPurpose" + "description": "The name of the site config." }, + "value": "logs" + }, + "resourceId": { + "type": "string", "metadata": { - "description": "Optional. The elastic pool SKU." - } + "description": "The resource ID of the site config." + }, + "value": "[resourceId('Microsoft.Web/sites/config', parameters('appName'), 'logs')]" }, - "autoPauseDelay": { - "type": "int", - "defaultValue": -1, + "resourceGroupName": { + "type": "string", "metadata": { - "description": "Optional. Time in minutes after which elastic pool is automatically paused. A value of -1 means that automatic pause is disabled." - } + "description": "The resource group the site config was deployed into." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "app", + "app_appsettings" + ] + }, + "app_websettings": { + "condition": "[not(empty(coalesce(parameters('webConfiguration'), createObject())))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Site-Config-Web', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "appName": { + "value": "[parameters('name')]" + }, + "webConfiguration": { + "value": "[parameters('webConfiguration')]" + } + }, + "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.33.93.31351", + "templateHash": "15058680643544097487" }, - "availabilityZone": { + "name": "Site Web Config", + "description": "This module deploys web settings configuration available under sites/config name: web." + }, + "parameters": { + "appName": { "type": "string", - "allowedValues": [ - "1", - "2", - "3", - "NoPreference" - ], - "defaultValue": "NoPreference", "metadata": { - "description": "Optional. Specifies the availability zone the pool's primary replica is pinned to." + "description": "Required. The name of the parent site resource." } }, - "highAvailabilityReplicaCount": { - "type": "int", + "webConfiguration": { + "type": "object", "nullable": true, "metadata": { - "description": "Optional. The number of secondary replicas associated with the elastic pool that are used to provide high availability. Applicable only to Hyperscale elastic pools." + "description": "Optional. The Site Config, Web settings to deploy." } + } + }, + "resources": { + "app": { + "existing": true, + "type": "Microsoft.Web/sites", + "apiVersion": "2024-04-01", + "name": "[parameters('appName')]" }, - "licenseType": { + "webSettings": { + "type": "Microsoft.Web/sites/config", + "apiVersion": "2024-04-01", + "name": "[format('{0}/{1}', parameters('appName'), 'web')]", + "kind": "string", + "properties": "[parameters('webConfiguration')]" + } + }, + "outputs": { + "name": { "type": "string", - "defaultValue": "LicenseIncluded", - "allowedValues": [ - "BasePrice", - "LicenseIncluded" - ], "metadata": { - "description": "Optional. The license type to apply for this elastic pool." - } + "description": "The name of the site config." + }, + "value": "web" }, - "maintenanceConfigurationId": { + "resourceId": { "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Maintenance configuration resource ID assigned to the elastic pool. This configuration defines the period when the maintenance updates will will occur." - } - }, - "maxSizeBytes": { - "type": "int", - "defaultValue": 34359738368, "metadata": { - "description": "Optional. The storage limit for the database elastic pool in bytes." - } + "description": "The resource ID of the site config." + }, + "value": "[resourceId('Microsoft.Web/sites/config', parameters('appName'), 'web')]" }, - "minCapacity": { - "type": "int", - "nullable": true, + "resourceGroupName": { + "type": "string", "metadata": { - "description": "Optional. Minimal capacity that serverless pool will not shrink below, if not paused." - } - }, - "perDatabaseSettings": { - "$ref": "#/definitions/elasticPoolPerDatabaseSettingsType", - "defaultValue": { - "autoPauseDelay": -1, - "maxCapacity": "2", - "minCapacity": "0" + "description": "The resource group the site config was deployed into." }, - "metadata": { - "description": "Optional. The per database settings for the elastic pool." - } + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "app" + ] + }, + "extension_msdeploy": { + "condition": "[not(empty(parameters('msDeployConfiguration')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Site-Extension-MSDeploy', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "appName": { + "value": "[parameters('name')]" + }, + "msDeployConfiguration": { + "value": "[coalesce(parameters('msDeployConfiguration'), createObject())]" + } + }, + "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.33.93.31351", + "templateHash": "14895622660217616811" }, - "preferredEnclaveType": { + "name": "Site Deployment Extension ", + "description": "This module deploys a Site extension for MSDeploy." + }, + "parameters": { + "appName": { "type": "string", - "allowedValues": [ - "Default", - "VBS" - ], - "defaultValue": "Default", "metadata": { - "description": "Optional. Type of enclave requested on the elastic pool." + "description": "Required. The name of the parent site resource." } }, - "zoneRedundant": { - "type": "bool", - "defaultValue": true, + "msDeployConfiguration": { + "type": "object", + "nullable": true, "metadata": { - "description": "Optional. Whether or not this elastic pool is zone redundant, which means the replicas of this elastic pool will be spread across multiple availability zones." + "description": "Optional. Sets the MSDeployment Properties." } } }, "resources": { - "server": { + "app": { "existing": true, - "type": "Microsoft.Sql/servers", - "apiVersion": "2023-08-01-preview", - "name": "[parameters('serverName')]" + "type": "Microsoft.Web/sites", + "apiVersion": "2024-04-01", + "name": "[parameters('appName')]" }, - "elasticPool": { - "type": "Microsoft.Sql/servers/elasticPools", - "apiVersion": "2023-08-01-preview", - "name": "[format('{0}/{1}', parameters('serverName'), parameters('name'))]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", - "sku": "[parameters('sku')]", - "properties": { - "autoPauseDelay": "[parameters('autoPauseDelay')]", - "availabilityZone": "[parameters('availabilityZone')]", - "highAvailabilityReplicaCount": "[parameters('highAvailabilityReplicaCount')]", - "licenseType": "[parameters('licenseType')]", - "maintenanceConfigurationId": "[parameters('maintenanceConfigurationId')]", - "maxSizeBytes": "[parameters('maxSizeBytes')]", - "minCapacity": "[parameters('minCapacity')]", - "perDatabaseSettings": "[if(not(empty(parameters('perDatabaseSettings'))), createObject('autoPauseDelay', tryGet(parameters('perDatabaseSettings'), 'autoPauseDelay'), 'maxCapacity', json(tryGet(parameters('perDatabaseSettings'), 'maxCapacity')), 'minCapacity', json(tryGet(parameters('perDatabaseSettings'), 'minCapacity'))), null())]", - "preferredEnclaveType": "[parameters('preferredEnclaveType')]", - "zoneRedundant": "[parameters('zoneRedundant')]" - } + "msdeploy": { + "type": "Microsoft.Web/sites/extensions", + "apiVersion": "2024-04-01", + "name": "[format('{0}/{1}', parameters('appName'), 'MSDeploy')]", + "kind": "MSDeploy", + "properties": "[parameters('msDeployConfiguration')]" } }, "outputs": { "name": { "type": "string", "metadata": { - "description": "The name of the deployed Elastic Pool." + "description": "The name of the MSDeploy Package." }, - "value": "[parameters('name')]" + "value": "MSDeploy" }, "resourceId": { "type": "string", "metadata": { - "description": "The resource ID of the deployed Elastic Pool." + "description": "The resource ID of the Site Extension." }, - "value": "[resourceId('Microsoft.Sql/servers/elasticPools', parameters('serverName'), parameters('name'))]" + "value": "[resourceId('Microsoft.Web/sites/extensions', parameters('appName'), 'MSDeploy')]" }, "resourceGroupName": { "type": "string", "metadata": { - "description": "The resource group of the deployed Elastic Pool." + "description": "The resource group the site config was deployed into." }, "value": "[resourceGroup().name]" - }, - "location": { - "type": "string", - "metadata": { - "description": "The location the resource was deployed into." - }, - "value": "[reference('elasticPool', '2023-08-01-preview', 'full').location]" } } } }, "dependsOn": [ - "server" + "app" ] }, - "server_privateEndpoints": { + "app_slots": { "copy": { - "name": "server_privateEndpoints", - "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]" + "name": "app_slots", + "count": "[length(coalesce(parameters('slots'), createArray()))]", + "mode": "serial", + "batchSize": 1 }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-server-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]]", + "name": "[format('{0}-Slot-{1}', uniqueString(deployment().name, parameters('location')), coalesce(parameters('slots'), createArray())[copyIndex()].name)]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -84654,42 +95337,130 @@ "mode": "Incremental", "parameters": { "name": { - "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'name'), format('pep-{0}-{1}-{2}', last(split(resourceId('Microsoft.Sql/servers', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'sqlServer'), copyIndex()))]" + "value": "[coalesce(parameters('slots'), createArray())[copyIndex()].name]" }, - "privateLinkServiceConnections": "[if(not(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true())), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.Sql/servers', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'sqlServer'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.Sql/servers', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'sqlServer')))))), createObject('value', null()))]", - "manualPrivateLinkServiceConnections": "[if(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true()), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.Sql/servers', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'sqlServer'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.Sql/servers', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'sqlServer')), 'requestMessage', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualConnectionRequestMessage'), 'Manual approval required.'))))), createObject('value', null()))]", - "subnetResourceId": { - "value": "[coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId]" - }, - "enableTelemetry": { - "value": "[variables('enableReferencedModulesTelemetry')]" + "appName": { + "value": "[parameters('name')]" }, "location": { - "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'location'), reference(split(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location)]" + "value": "[parameters('location')]" }, - "lock": { - "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" + "kind": { + "value": "[parameters('kind')]" }, - "privateDnsZoneGroup": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroup')]" + "serverFarmResourceId": { + "value": "[parameters('serverFarmResourceId')]" + }, + "httpsOnly": { + "value": "[coalesce(tryGet(coalesce(parameters('slots'), createArray())[copyIndex()], 'httpsOnly'), parameters('httpsOnly'))]" + }, + "appServiceEnvironmentResourceId": { + "value": "[parameters('appServiceEnvironmentResourceId')]" + }, + "clientAffinityEnabled": { + "value": "[coalesce(tryGet(coalesce(parameters('slots'), createArray())[copyIndex()], 'clientAffinityEnabled'), parameters('clientAffinityEnabled'))]" + }, + "managedIdentities": { + "value": "[coalesce(tryGet(coalesce(parameters('slots'), createArray())[copyIndex()], 'managedIdentities'), parameters('managedIdentities'))]" + }, + "keyVaultAccessIdentityResourceId": { + "value": "[coalesce(tryGet(coalesce(parameters('slots'), createArray())[copyIndex()], 'keyVaultAccessIdentityResourceId'), parameters('keyVaultAccessIdentityResourceId'))]" + }, + "storageAccountRequired": { + "value": "[coalesce(tryGet(coalesce(parameters('slots'), createArray())[copyIndex()], 'storageAccountRequired'), parameters('storageAccountRequired'))]" + }, + "virtualNetworkSubnetId": { + "value": "[coalesce(tryGet(coalesce(parameters('slots'), createArray())[copyIndex()], 'virtualNetworkSubnetId'), parameters('virtualNetworkSubnetId'))]" + }, + "siteConfig": { + "value": "[coalesce(tryGet(coalesce(parameters('slots'), createArray())[copyIndex()], 'siteConfig'), parameters('siteConfig'))]" + }, + "functionAppConfig": { + "value": "[coalesce(tryGet(coalesce(parameters('slots'), createArray())[copyIndex()], 'functionAppConfig'), parameters('functionAppConfig'))]" + }, + "storageAccountResourceId": { + "value": "[coalesce(tryGet(coalesce(parameters('slots'), createArray())[copyIndex()], 'storageAccountResourceId'), parameters('storageAccountResourceId'))]" + }, + "storageAccountUseIdentityAuthentication": { + "value": "[coalesce(tryGet(coalesce(parameters('slots'), createArray())[copyIndex()], 'storageAccountUseIdentityAuthentication'), parameters('storageAccountUseIdentityAuthentication'))]" + }, + "appInsightResourceId": { + "value": "[coalesce(tryGet(coalesce(parameters('slots'), createArray())[copyIndex()], 'appInsightResourceId'), parameters('appInsightResourceId'))]" + }, + "authSettingV2Configuration": { + "value": "[coalesce(tryGet(coalesce(parameters('slots'), createArray())[copyIndex()], 'authSettingV2Configuration'), parameters('authSettingV2Configuration'))]" + }, + "msDeployConfiguration": { + "value": "[coalesce(tryGet(coalesce(parameters('slots'), createArray())[copyIndex()], 'msDeployConfiguration'), parameters('msDeployConfiguration'))]" + }, + "diagnosticSettings": { + "value": "[tryGet(coalesce(parameters('slots'), createArray())[copyIndex()], 'diagnosticSettings')]" }, "roleAssignments": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'roleAssignments')]" + "value": "[tryGet(coalesce(parameters('slots'), createArray())[copyIndex()], 'roleAssignments')]" + }, + "appSettingsKeyValuePairs": { + "value": "[coalesce(tryGet(coalesce(parameters('slots'), createArray())[copyIndex()], 'appSettingsKeyValuePairs'), parameters('appSettingsKeyValuePairs'))]" + }, + "basicPublishingCredentialsPolicies": { + "value": "[coalesce(tryGet(coalesce(parameters('slots'), createArray())[copyIndex()], 'basicPublishingCredentialsPolicies'), parameters('basicPublishingCredentialsPolicies'))]" + }, + "lock": { + "value": "[coalesce(tryGet(coalesce(parameters('slots'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" + }, + "privateEndpoints": { + "value": "[coalesce(tryGet(coalesce(parameters('slots'), createArray())[copyIndex()], 'privateEndpoints'), createArray())]" }, "tags": { - "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + "value": "[coalesce(tryGet(coalesce(parameters('slots'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" }, - "customDnsConfigs": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customDnsConfigs')]" + "clientCertEnabled": { + "value": "[tryGet(coalesce(parameters('slots'), createArray())[copyIndex()], 'clientCertEnabled')]" }, - "ipConfigurations": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'ipConfigurations')]" + "clientCertExclusionPaths": { + "value": "[tryGet(coalesce(parameters('slots'), createArray())[copyIndex()], 'clientCertExclusionPaths')]" }, - "applicationSecurityGroupResourceIds": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'applicationSecurityGroupResourceIds')]" + "clientCertMode": { + "value": "[tryGet(coalesce(parameters('slots'), createArray())[copyIndex()], 'clientCertMode')]" }, - "customNetworkInterfaceName": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customNetworkInterfaceName')]" + "cloningInfo": { + "value": "[tryGet(coalesce(parameters('slots'), createArray())[copyIndex()], 'cloningInfo')]" + }, + "containerSize": { + "value": "[tryGet(coalesce(parameters('slots'), createArray())[copyIndex()], 'containerSize')]" + }, + "customDomainVerificationId": { + "value": "[tryGet(coalesce(parameters('slots'), createArray())[copyIndex()], 'customDomainVerificationId')]" + }, + "dailyMemoryTimeQuota": { + "value": "[tryGet(coalesce(parameters('slots'), createArray())[copyIndex()], 'dailyMemoryTimeQuota')]" + }, + "enabled": { + "value": "[tryGet(coalesce(parameters('slots'), createArray())[copyIndex()], 'enabled')]" + }, + "hostNameSslStates": { + "value": "[tryGet(coalesce(parameters('slots'), createArray())[copyIndex()], 'hostNameSslStates')]" + }, + "hyperV": { + "value": "[tryGet(coalesce(parameters('slots'), createArray())[copyIndex()], 'hyperV')]" + }, + "publicNetworkAccess": { + "value": "[coalesce(tryGet(coalesce(parameters('slots'), createArray())[copyIndex()], 'publicNetworkAccess'), if(or(not(empty(tryGet(coalesce(parameters('slots'), createArray())[copyIndex()], 'privateEndpoints'))), not(empty(parameters('privateEndpoints')))), 'Disabled', 'Enabled'))]" + }, + "redundancyMode": { + "value": "[tryGet(coalesce(parameters('slots'), createArray())[copyIndex()], 'redundancyMode')]" + }, + "vnetContentShareEnabled": { + "value": "[tryGet(coalesce(parameters('slots'), createArray())[copyIndex()], 'vnetContentShareEnabled')]" + }, + "vnetImagePullEnabled": { + "value": "[tryGet(coalesce(parameters('slots'), createArray())[copyIndex()], 'vnetImagePullEnabled')]" + }, + "vnetRouteAllEnabled": { + "value": "[tryGet(coalesce(parameters('slots'), createArray())[copyIndex()], 'vnetRouteAllEnabled')]" + }, + "hybridConnectionRelays": { + "value": "[tryGet(coalesce(parameters('slots'), createArray())[copyIndex()], 'hybridConnectionRelays')]" } }, "template": { @@ -84699,30 +95470,69 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "15954548978129725136" + "version": "0.33.93.31351", + "templateHash": "4067755327331248181" }, - "name": "Private Endpoints", - "description": "This module deploys a Private Endpoint." + "name": "Web/Function App Deployment Slots", + "description": "This module deploys a Web or Function App Deployment Slot." }, "definitions": { - "privateDnsZoneGroupType": { + "privateEndpointOutputType": { "type": "object", "properties": { "name": { + "type": "string", + "metadata": { + "description": "The name of the private endpoint." + } + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the private endpoint." + } + }, + "groupId": { "type": "string", "nullable": true, "metadata": { - "description": "Optional. The name of the Private DNS Zone Group." + "description": "The group Id for the private endpoint Group." } }, - "privateDnsZoneGroupConfigs": { + "customDnsConfigs": { "type": "array", "items": { - "$ref": "#/definitions/privateDnsZoneGroupConfigType" + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "FQDN that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "A list of private IP addresses of the private endpoint." + } + } + } }, "metadata": { - "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + "description": "The custom DNS configurations of the private endpoint." + } + }, + "networkInterfaceResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "The IDs of the network interfaces associated with the private endpoint." } } }, @@ -84730,7 +95540,33 @@ "__bicep_export!": true } }, - "ipConfigurationType": { + "_1.privateEndpointCustomDnsConfigType": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. FQDN that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "_1.privateEndpointIpConfigurationType": { "type": "object", "properties": { "name": { @@ -84745,13 +95581,13 @@ "groupId": { "type": "string", "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." } }, "memberName": { "type": "string", "metadata": { - "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to." } }, "privateIPAddress": { @@ -84762,80 +95598,177 @@ } }, "metadata": { - "description": "Required. Properties of private endpoint IP configurations." + "description": "Required. Properties of private endpoint IP configurations." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "_1.privateEndpointPrivateDnsZoneGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "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": "Required. The private DNS Zone Groups to associate the Private Endpoint. A DNS Zone Group can support up to 5 DNS zones." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "diagnosticSettingFullType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "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 logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "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." } - } - }, - "metadata": { - "__bicep_export!": true - } - }, - "privateLinkServiceConnectionType": { - "type": "object", - "properties": { - "name": { + }, + "logAnalyticsDestinationType": { "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, "metadata": { - "description": "Required. The name of the private link service connection." + "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." } }, - "properties": { - "type": "object", - "properties": { - "groupIds": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." - } - }, - "privateLinkServiceId": { - "type": "string", - "metadata": { - "description": "Required. The resource id of private link service." - } - }, - "requestMessage": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." - } - } - }, + "workspaceResourceId": { + "type": "string", + "nullable": true, "metadata": { - "description": "Required. Properties of private link service connection." + "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." } - } - }, - "metadata": { - "__bicep_export!": true - } - }, - "customDnsConfigType": { - "type": "object", - "properties": { - "fqdn": { + }, + "storageAccountResourceId": { "type": "string", "nullable": true, "metadata": { - "description": "Optional. FQDN that resolves to private endpoint IP address." + "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." } }, - "ipAddresses": { - "type": "array", - "items": { - "type": "string" - }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, "metadata": { - "description": "Required. A list of private IP addresses of the private endpoint." + "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": { - "__bicep_export!": true + "description": "An AVM-aligned type for a diagnostic setting. To be used if both logs & metrics are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } } }, "lockType": { @@ -84868,1422 +95801,2316 @@ } } }, - "privateDnsZoneGroupConfigType": { + "managedIdentityAllType": { "type": "object", "properties": { - "name": { - "type": "string", + "systemAssigned": { + "type": "bool", "nullable": true, "metadata": { - "description": "Optional. The name of the private DNS zone group config." + "description": "Optional. Enables system assigned managed identity on the resource." } }, - "privateDnsZoneResourceId": { - "type": "string", + "userAssignedResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, "metadata": { - "description": "Required. The resource id of the private DNS zone." + "description": "Optional. The resource ID(s) to assign to the resource. Required if a user assigned identity is used for encryption." } } }, "metadata": { + "description": "An AVM-aligned type for a managed identity configuration. To be used if both a system-assigned & user-assigned identities are supported by the resource provider.", "__bicep_imported_from!": { - "sourceTemplate": "private-dns-zone-group/main.bicep" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } }, - "roleAssignmentType": { + "privateEndpointSingleServiceType": { "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." + "description": "Optional. The name of the Private Endpoint." } }, - "roleDefinitionIdOrName": { + "location": { "type": "string", + "nullable": true, "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'." + "description": "Optional. The location to deploy the Private Endpoint to." } }, - "principalId": { + "privateLinkServiceConnectionName": { "type": "string", + "nullable": true, "metadata": { - "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + "description": "Optional. The name of the private link connection to create." } }, - "principalType": { + "service": { "type": "string", - "allowedValues": [ - "Device", - "ForeignGroup", - "Group", - "ServicePrincipal", - "User" - ], "nullable": true, "metadata": { - "description": "Optional. The principal type of the assigned principal ID." + "description": "Optional. The subresource to deploy the Private Endpoint for. For example \"vault\" for a Key Vault Private Endpoint." } }, - "description": { + "subnetResourceId": { "type": "string", - "nullable": true, "metadata": { - "description": "Optional. The description of the role assignment." + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." } }, - "condition": { + "resourceGroupResourceId": { "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\"." + "description": "Optional. The resource ID of the Resource Group the Private Endpoint will be created in. If not specified, the Resource Group of the provided Virtual Network Subnet is used." } }, - "conditionVersion": { + "privateDnsZoneGroup": { + "$ref": "#/definitions/_1.privateEndpointPrivateDnsZoneGroupType", + "nullable": true, + "metadata": { + "description": "Optional. The private DNS Zone Group to configure for the Private Endpoint." + } + }, + "isManualConnection": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. If Manual Private Link Connection is required." + } + }, + "manualConnectionRequestMessage": { "type": "string", - "allowedValues": [ - "2.0" - ], "nullable": true, + "maxLength": 140, "metadata": { - "description": "Optional. Version of the condition." + "description": "Optional. A message passed to the owner of the remote resource with the manual connection request." } }, - "delegatedManagedIdentityResourceId": { + "customDnsConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/_1.privateEndpointCustomDnsConfigType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Custom DNS configurations." + } + }, + "ipConfigurations": { + "type": "array", + "items": { + "$ref": "#/definitions/_1.privateEndpointIpConfigurationType" + }, + "nullable": true, + "metadata": { + "description": "Optional. A list of IP configurations of the Private Endpoint. This will be used to map to the first-party Service endpoints." + } + }, + "applicationSecurityGroupResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Application security groups in which the Private Endpoint IP configuration is included." + } + }, + "customNetworkInterfaceName": { "type": "string", "nullable": true, "metadata": { - "description": "Optional. The Resource Id of the delegated managed identity resource." + "description": "Optional. The custom name of the network interface attached to the Private Endpoint." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags to be applied on all resources/Resource Groups in this deployment." + } + }, + "enableTelemetry": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." } } }, "metadata": { - "description": "An AVM-aligned type for a role assignment.", + "description": "An AVM-aligned type for a private endpoint. To be used if the private endpoint's default service / groupId can be assumed (i.e., for services that only have one Private Endpoint type like 'vault' for key vault).", "__bicep_imported_from!": { "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" } } - } - }, - "parameters": { - "name": { - "type": "string", - "metadata": { - "description": "Required. Name of the private endpoint resource to create." - } - }, - "subnetResourceId": { - "type": "string", - "metadata": { - "description": "Required. Resource ID of the subnet where the endpoint needs to be created." - } - }, - "applicationSecurityGroupResourceIds": { - "type": "array", - "items": { - "type": "string" - }, - "nullable": true, - "metadata": { - "description": "Optional. Application security groups in which the private endpoint IP configuration is included." - } - }, - "customNetworkInterfaceName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The custom name of the network interface attached to the private endpoint." - } - }, - "ipConfigurations": { - "type": "array", - "items": { - "$ref": "#/definitions/ipConfigurationType" - }, - "nullable": true, - "metadata": { - "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." - } }, - "privateDnsZoneGroup": { - "$ref": "#/definitions/privateDnsZoneGroupType", - "nullable": true, - "metadata": { - "description": "Optional. The private DNS zone group to configure for the private endpoint." - } - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]", - "metadata": { - "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": { + "roleAssignmentType": { "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." - } - }, - "customDnsConfigs": { - "type": "array", - "items": { - "$ref": "#/definitions/customDnsConfigType" - }, - "nullable": true, - "metadata": { - "description": "Optional. Custom DNS configurations." - } - }, - "manualPrivateLinkServiceConnections": { - "type": "array", - "items": { - "$ref": "#/definitions/privateLinkServiceConnectionType" - }, - "nullable": true, - "metadata": { - "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." - } - }, - "privateLinkServiceConnections": { - "type": "array", - "items": { - "$ref": "#/definitions/privateLinkServiceConnectionType" - }, - "nullable": true, - "metadata": { - "description": "Conditional. A grouping of information about the connection to the remote resource. Required if `manualPrivateLinkServiceConnections` is empty." - } - }, - "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": "2024-03-01", - "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.10.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" - } + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." } - } - } - }, - "privateEndpoint": { - "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2023-11-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')]]" - } + }, + "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\"." } - ], - "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": "[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": "2022-09-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]" + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." } }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "languageVersion": "2.0", - "contentVersion": "1.0.0.0", + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, "metadata": { - "_generator": { - "name": "bicep", - "version": "0.33.13.18514", - "templateHash": "5440815542537978381" - }, - "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 - } - } - }, - "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." - } - } - }, - "variables": { - "copy": [ - { - "name": "privateDnsZoneConfigsVar", - "count": "[length(parameters('privateDnsZoneConfigs'))]", - "input": { - "name": "[coalesce(tryGet(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')], 'name'), last(split(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId, '/')))]", - "properties": { - "privateDnsZoneId": "[parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId]" - } - } - } - ] - }, - "resources": { - "privateEndpoint": { - "existing": true, - "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2023-11-01", - "name": "[parameters('privateEndpointName')]" - }, - "privateDnsZoneGroup": { - "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2023-11-01", - "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", - "properties": { - "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" - } - } - }, - "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]" - } + "description": "Optional. The Resource Id of the delegated managed identity resource." } } }, - "dependsOn": [ - "privateEndpoint" - ] + "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.5.1" + } + } } }, - "outputs": { - "resourceGroupName": { + "parameters": { + "name": { "type": "string", "metadata": { - "description": "The resource group the private endpoint was deployed into." - }, - "value": "[resourceGroup().name]" + "description": "Required. Name of the slot." + } }, - "resourceId": { + "appName": { "type": "string", "metadata": { - "description": "The resource ID of the private endpoint." - }, - "value": "[resourceId('Microsoft.Network/privateEndpoints', parameters('name'))]" + "description": "Conditional. The name of the parent site resource. Required if the template is used in a standalone deployment." + } }, - "name": { + "location": { "type": "string", + "defaultValue": "[resourceGroup().location]", "metadata": { - "description": "The name of the private endpoint." - }, - "value": "[parameters('name')]" + "description": "Optional. Location for all Resources." + } }, - "location": { + "kind": { "type": "string", + "allowedValues": [ + "functionapp", + "functionapp,linux", + "functionapp,workflowapp", + "functionapp,workflowapp,linux", + "functionapp,linux,container", + "functionapp,linux,container,azurecontainerapps", + "app,linux", + "app", + "linux,api", + "api", + "app,linux,container", + "app,container,windows" + ], "metadata": { - "description": "The location the resource was deployed into." - }, - "value": "[reference('privateEndpoint', '2023-11-01', 'full').location]" + "description": "Required. Type of site to deploy." + } }, - "customDnsConfigs": { - "type": "array", - "items": { - "$ref": "#/definitions/customDnsConfigType" - }, + "serverFarmResourceId": { + "type": "string", + "nullable": true, "metadata": { - "description": "The custom DNS configurations of the private endpoint." - }, - "value": "[reference('privateEndpoint').customDnsConfigs]" + "description": "Optional. The resource ID of the app service plan to use for the slot." + } }, - "networkInterfaceResourceIds": { - "type": "array", - "items": { - "type": "string" - }, + "httpsOnly": { + "type": "bool", + "defaultValue": true, "metadata": { - "description": "The resource IDs of the network interfaces associated with the private endpoint." - }, - "value": "[map(reference('privateEndpoint').networkInterfaces, lambda('nic', lambdaVariables('nic').id))]" + "description": "Optional. Configures a slot to accept only HTTPS requests. Issues redirect for HTTP requests." + } }, - "groupId": { - "type": "string", - "nullable": true, + "clientAffinityEnabled": { + "type": "bool", + "defaultValue": 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": [ - "server" - ] - }, - "server_firewallRules": { - "copy": { - "name": "server_firewallRules", - "count": "[length(parameters('firewallRules'))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-Sql-FirewallRules-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[parameters('firewallRules')[copyIndex()].name]" - }, - "serverName": { - "value": "[parameters('name')]" - }, - "endIpAddress": { - "value": "[tryGet(parameters('firewallRules')[copyIndex()], 'endIpAddress')]" - }, - "startIpAddress": { - "value": "[tryGet(parameters('firewallRules')[copyIndex()], 'startIpAddress')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.34.44.8038", - "templateHash": "7783790094003665329" + "description": "Optional. If client affinity is enabled." + } }, - "name": "Azure SQL Server Firewall Rule", - "description": "This module deploys an Azure SQL Server Firewall Rule." - }, - "parameters": { - "name": { + "appServiceEnvironmentResourceId": { "type": "string", + "nullable": true, "metadata": { - "description": "Required. The name of the Server Firewall Rule." + "description": "Optional. The resource ID of the app service environment to use for this resource." } }, - "endIpAddress": { - "type": "string", - "defaultValue": "0.0.0.0", + "managedIdentities": { + "$ref": "#/definitions/managedIdentityAllType", + "nullable": true, "metadata": { - "description": "Optional. The end IP address of the firewall rule. Must be IPv4 format. Must be greater than or equal to startIpAddress. Use value '0.0.0.0' for all Azure-internal IP addresses." + "description": "Optional. The managed identity definition for this resource." } }, - "startIpAddress": { + "keyVaultAccessIdentityResourceId": { "type": "string", - "defaultValue": "0.0.0.0", + "nullable": true, "metadata": { - "description": "Optional. The start IP address of the firewall rule. Must be IPv4 format. Use value '0.0.0.0' for all Azure-internal IP addresses." + "description": "Optional. The resource ID of the assigned identity to be used to access a key vault with." } }, - "serverName": { - "type": "string", + "storageAccountRequired": { + "type": "bool", + "defaultValue": false, "metadata": { - "description": "Conditional. The name of the parent SQL Server. Required if the template is used in a standalone deployment." - } - } - }, - "resources": [ - { - "type": "Microsoft.Sql/servers/firewallRules", - "apiVersion": "2023-08-01-preview", - "name": "[format('{0}/{1}', parameters('serverName'), parameters('name'))]", - "properties": { - "endIpAddress": "[parameters('endIpAddress')]", - "startIpAddress": "[parameters('startIpAddress')]" + "description": "Optional. Checks if Customer provided storage account is required." } - } - ], - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the deployed firewall rule." - }, - "value": "[parameters('name')]" }, - "resourceId": { + "virtualNetworkSubnetId": { "type": "string", + "nullable": true, "metadata": { - "description": "The resource ID of the deployed firewall rule." + "description": "Optional. Azure Resource Manager ID of the Virtual network and subnet to be joined by Regional VNET Integration. This must be of the form /subscriptions/{subscriptionName}/resourceGroups/{resourceGroupName}/providers/Microsoft.Network/virtualNetworks/{vnetName}/subnets/{subnetName}." + } + }, + "siteConfig": { + "type": "object", + "defaultValue": { + "alwaysOn": true }, - "value": "[resourceId('Microsoft.Sql/servers/firewallRules', parameters('serverName'), parameters('name'))]" + "metadata": { + "description": "Optional. The site config object." + } }, - "resourceGroupName": { - "type": "string", + "functionAppConfig": { + "type": "object", + "nullable": true, "metadata": { - "description": "The resource group of the deployed firewall rule." - }, - "value": "[resourceGroup().name]" - } - } - } - }, - "dependsOn": [ - "server" - ] - }, - "server_virtualNetworkRules": { - "copy": { - "name": "server_virtualNetworkRules", - "count": "[length(parameters('virtualNetworkRules'))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-Sql-VirtualNetworkRules-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "serverName": { - "value": "[parameters('name')]" - }, - "name": { - "value": "[parameters('virtualNetworkRules')[copyIndex()].name]" - }, - "ignoreMissingVnetServiceEndpoint": { - "value": "[tryGet(parameters('virtualNetworkRules')[copyIndex()], 'ignoreMissingVnetServiceEndpoint')]" - }, - "virtualNetworkSubnetId": { - "value": "[parameters('virtualNetworkRules')[copyIndex()].virtualNetworkSubnetId]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.34.44.8038", - "templateHash": "529105308382514230" + "description": "Optional. The Function App config object." + } }, - "name": "Azure SQL Server Virtual Network Rules", - "description": "This module deploys an Azure SQL Server Virtual Network Rule." - }, - "parameters": { - "name": { + "storageAccountResourceId": { "type": "string", + "nullable": true, "metadata": { - "description": "Required. The name of the Server Virtual Network Rule." + "description": "Optional. Required if app of kind functionapp. Resource ID of the storage account to manage triggers and logging function executions." } }, - "ignoreMissingVnetServiceEndpoint": { + "storageAccountUseIdentityAuthentication": { "type": "bool", "defaultValue": false, "metadata": { - "description": "Optional. Allow creating a firewall rule before the virtual network has vnet service endpoint enabled." + "description": "Optional. If the provided storage account requires Identity based authentication ('allowSharedKeyAccess' is set to false). When set to true, the minimum role assignment required for the App Service Managed Identity to the storage account is 'Storage Blob Data Owner'." } }, - "virtualNetworkSubnetId": { + "appInsightResourceId": { "type": "string", + "nullable": true, "metadata": { - "description": "Required. The resource ID of the virtual network subnet." + "description": "Optional. Resource ID of the app insight to leverage for this resource." } }, - "serverName": { - "type": "string", + "appSettingsKeyValuePairs": { + "type": "object", + "nullable": true, "metadata": { - "description": "Conditional. The name of the parent SQL Server. Required if the template is used in a standalone deployment." + "description": "Optional. The app settings-value pairs except for AzureWebJobsStorage, AzureWebJobsDashboard, APPINSIGHTS_INSTRUMENTATIONKEY and APPLICATIONINSIGHTS_CONNECTION_STRING." } - } - }, - "resources": [ - { - "type": "Microsoft.Sql/servers/virtualNetworkRules", - "apiVersion": "2023-08-01-preview", - "name": "[format('{0}/{1}', parameters('serverName'), parameters('name'))]", - "properties": { - "ignoreMissingVnetServiceEndpoint": "[parameters('ignoreMissingVnetServiceEndpoint')]", - "virtualNetworkSubnetId": "[parameters('virtualNetworkSubnetId')]" + }, + "authSettingV2Configuration": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The auth settings V2 configuration." } - } - ], - "outputs": { - "name": { - "type": "string", + }, + "msDeployConfiguration": { + "type": "object", + "nullable": true, "metadata": { - "description": "The name of the deployed virtual network rule." - }, - "value": "[parameters('name')]" + "description": "Optional. The extension MSDeployment configuration." + } }, - "resourceId": { - "type": "string", + "lock": { + "$ref": "#/definitions/lockType", + "nullable": true, "metadata": { - "description": "The resource ID of the deployed virtual network rule." + "description": "Optional. The lock settings of the service." + } + }, + "privateEndpoints": { + "type": "array", + "items": { + "$ref": "#/definitions/privateEndpointSingleServiceType" }, - "value": "[resourceId('Microsoft.Sql/servers/virtualNetworkRules', parameters('serverName'), parameters('name'))]" + "nullable": true, + "metadata": { + "description": "Optional. Configuration details for private endpoints." + } }, - "resourceGroupName": { - "type": "string", + "tags": { + "type": "object", + "nullable": true, "metadata": { - "description": "The resource group of the deployed virtual network rule." + "description": "Optional. Tags of the resource." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" }, - "value": "[resourceGroup().name]" - } - } - } - }, - "dependsOn": [ - "server" - ] - }, - "server_securityAlertPolicies": { - "copy": { - "name": "server_securityAlertPolicies", - "count": "[length(parameters('securityAlertPolicies'))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-Sql-SecAlertPolicy-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[parameters('securityAlertPolicies')[copyIndex()].name]" - }, - "serverName": { - "value": "[parameters('name')]" - }, - "disabledAlerts": { - "value": "[tryGet(parameters('securityAlertPolicies')[copyIndex()], 'disabledAlerts')]" - }, - "emailAccountAdmins": { - "value": "[tryGet(parameters('securityAlertPolicies')[copyIndex()], 'emailAccountAdmins')]" - }, - "emailAddresses": { - "value": "[tryGet(parameters('securityAlertPolicies')[copyIndex()], 'emailAddresses')]" - }, - "retentionDays": { - "value": "[tryGet(parameters('securityAlertPolicies')[copyIndex()], 'retentionDays')]" - }, - "state": { - "value": "[tryGet(parameters('securityAlertPolicies')[copyIndex()], 'state')]" - }, - "storageAccountAccessKey": { - "value": "[tryGet(parameters('securityAlertPolicies')[copyIndex()], 'storageAccountAccessKey')]" - }, - "storageEndpoint": { - "value": "[tryGet(parameters('securityAlertPolicies')[copyIndex()], 'storageEndpoint')]" - } - }, - "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.34.44.8038", - "templateHash": "5426873131651303318" + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } }, - "name": "Azure SQL Server Security Alert Policies", - "description": "This module deploys an Azure SQL Server Security Alert Policy." - }, - "parameters": { - "name": { + "diagnosticSettings": { + "type": "array", + "items": { + "$ref": "#/definitions/diagnosticSettingFullType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "clientCertEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. To enable client certificate authentication (TLS mutual authentication)." + } + }, + "clientCertExclusionPaths": { "type": "string", + "nullable": true, "metadata": { - "description": "Required. The name of the Security Alert Policy." + "description": "Optional. Client certificate authentication comma-separated exclusion paths." } }, - "disabledAlerts": { - "type": "array", - "items": { - "type": "string" - }, - "defaultValue": [], + "clientCertMode": { + "type": "string", + "defaultValue": "Optional", "allowedValues": [ - "Sql_Injection", - "Sql_Injection_Vulnerability", - "Access_Anomaly", - "Data_Exfiltration", - "Unsafe_Action", - "Brute_Force" + "Optional", + "OptionalInteractiveUser", + "Required" ], "metadata": { - "description": "Optional. Alerts to disable." + "description": "Optional. This composes with ClientCertEnabled setting.

- ClientCertEnabled: false means ClientCert is ignored.

- ClientCertEnabled: true and ClientCertMode: Required means ClientCert is required.

- ClientCertEnabled: true and ClientCertMode: Optional means ClientCert is optional or accepted." } }, - "emailAccountAdmins": { + "cloningInfo": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. If specified during app creation, the app is cloned from a source app." + } + }, + "containerSize": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Size of the function container." + } + }, + "customDomainVerificationId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Unique identifier that verifies the custom domains assigned to the app. Customer will add this ID to a txt record for verification." + } + }, + "dailyMemoryTimeQuota": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Maximum allowed daily memory-time quota (applicable on dynamic apps only)." + } + }, + "enabled": { "type": "bool", - "defaultValue": false, + "defaultValue": true, "metadata": { - "description": "Optional. Specifies that the alert is sent to the account administrators." + "description": "Optional. Setting this value to false disables the app (takes the app offline)." } }, - "emailAddresses": { + "hostNameSslStates": { "type": "array", - "items": { - "type": "string" - }, - "defaultValue": [], + "nullable": true, "metadata": { - "description": "Optional. Specifies an array of email addresses to which the alert is sent." + "description": "Optional. Hostname SSL states are used to manage the SSL bindings for app's hostnames." } }, - "retentionDays": { - "type": "int", - "defaultValue": 0, + "hyperV": { + "type": "bool", + "defaultValue": false, "metadata": { - "description": "Optional. Specifies the number of days to keep in the Threat Detection audit logs." + "description": "Optional. Hyper-V sandbox." } }, - "state": { + "publicNetworkAccess": { "type": "string", - "defaultValue": "Disabled", + "nullable": true, "allowedValues": [ - "Disabled", - "Enabled" + "Enabled", + "Disabled" ], "metadata": { - "description": "Optional. Specifies the state of the policy, whether it is enabled or disabled or a policy has not been applied yet on the specific database." + "description": "Optional. Allow or block all public traffic." } }, - "storageAccountAccessKey": { - "type": "securestring", - "nullable": true, + "redundancyMode": { + "type": "string", + "defaultValue": "None", + "allowedValues": [ + "ActiveActive", + "Failover", + "GeoRedundant", + "Manual", + "None" + ], "metadata": { - "description": "Optional. Specifies the identifier key of the Threat Detection audit storage account." + "description": "Optional. Site redundancy mode." } }, - "storageEndpoint": { - "type": "string", + "basicPublishingCredentialsPolicies": { + "type": "array", "nullable": true, "metadata": { - "description": "Optional. Specifies the blob storage endpoint. This blob storage will hold all Threat Detection audit logs." + "description": "Optional. The site publishing credential policy names which are associated with the site slot." } }, - "serverName": { - "type": "string", + "vnetContentShareEnabled": { + "type": "bool", + "defaultValue": false, "metadata": { - "description": "Conditional. The name of the parent SQL Server. Required if the template is used in a standalone deployment." + "description": "Optional. To enable accessing content over virtual network." + } + }, + "vnetImagePullEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. To enable pulling image over Virtual Network." + } + }, + "vnetRouteAllEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Virtual Network Route All enabled. This causes all outbound traffic to have Virtual Network Security Groups and User Defined Routes applied." + } + }, + "hybridConnectionRelays": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. Names of hybrid connection relays to connect app with." + } + } + }, + "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)))))]" } + ], + "enableReferencedModulesTelemetry": false, + "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", + "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned, UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', null())), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", + "builtInRoleNames": { + "App Compliance Automation Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f37683f-2463-46b6-9ce7-9b788b988ba2')]", + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]", + "Web Plan Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2cc479cb-7b4d-49a8-b449-8c00fd0f0a4b')]", + "Website Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'de139f84-1756-47ae-9be6-808fbbe84772')]" } }, "resources": { - "server": { + "app": { "existing": true, - "type": "Microsoft.Sql/servers", - "apiVersion": "2023-08-01-preview", - "name": "[parameters('serverName')]" + "type": "Microsoft.Web/sites", + "apiVersion": "2024-04-01", + "name": "[parameters('appName')]" + }, + "slot": { + "type": "Microsoft.Web/sites/slots", + "apiVersion": "2024-04-01", + "name": "[format('{0}/{1}', parameters('appName'), parameters('name'))]", + "location": "[parameters('location')]", + "kind": "[parameters('kind')]", + "tags": "[parameters('tags')]", + "identity": "[variables('identity')]", + "properties": { + "serverFarmId": "[parameters('serverFarmResourceId')]", + "clientAffinityEnabled": "[parameters('clientAffinityEnabled')]", + "httpsOnly": "[parameters('httpsOnly')]", + "hostingEnvironmentProfile": "[if(not(empty(parameters('appServiceEnvironmentResourceId'))), createObject('id', parameters('appServiceEnvironmentResourceId')), null())]", + "storageAccountRequired": "[parameters('storageAccountRequired')]", + "keyVaultReferenceIdentity": "[parameters('keyVaultAccessIdentityResourceId')]", + "virtualNetworkSubnetId": "[parameters('virtualNetworkSubnetId')]", + "siteConfig": "[parameters('siteConfig')]", + "functionAppConfig": "[parameters('functionAppConfig')]", + "clientCertEnabled": "[parameters('clientCertEnabled')]", + "clientCertExclusionPaths": "[parameters('clientCertExclusionPaths')]", + "clientCertMode": "[parameters('clientCertMode')]", + "cloningInfo": "[parameters('cloningInfo')]", + "containerSize": "[parameters('containerSize')]", + "customDomainVerificationId": "[parameters('customDomainVerificationId')]", + "dailyMemoryTimeQuota": "[parameters('dailyMemoryTimeQuota')]", + "enabled": "[parameters('enabled')]", + "hostNameSslStates": "[parameters('hostNameSslStates')]", + "hyperV": "[parameters('hyperV')]", + "publicNetworkAccess": "[parameters('publicNetworkAccess')]", + "redundancyMode": "[parameters('redundancyMode')]", + "vnetContentShareEnabled": "[parameters('vnetContentShareEnabled')]", + "vnetImagePullEnabled": "[parameters('vnetImagePullEnabled')]", + "vnetRouteAllEnabled": "[parameters('vnetRouteAllEnabled')]" + } + }, + "slot_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.Web/sites/{0}/slots/{1}', parameters('appName'), parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "slot" + ] }, - "securityAlertPolicy": { - "type": "Microsoft.Sql/servers/securityAlertPolicies", - "apiVersion": "2023-08-01-preview", - "name": "[format('{0}/{1}', parameters('serverName'), parameters('name'))]", + "slot_diagnosticSettings": { + "copy": { + "name": "slot_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Web/sites/{0}/slots/{1}', parameters('appName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", "properties": { - "disabledAlerts": "[parameters('disabledAlerts')]", - "emailAccountAdmins": "[parameters('emailAccountAdmins')]", - "emailAddresses": "[parameters('emailAddresses')]", - "retentionDays": "[parameters('retentionDays')]", - "state": "[parameters('state')]", - "storageAccountAccessKey": "[parameters('storageAccountAccessKey')]", - "storageEndpoint": "[parameters('storageEndpoint')]" - } - } - }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the deployed security alert policy." + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null + } + }, + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" }, - "value": "[parameters('name')]" + "dependsOn": [ + "slot" + ] }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the deployed security alert policy." + "slot_roleAssignments": { + "copy": { + "name": "slot_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, - "value": "[resourceId('Microsoft.Sql/servers/securityAlertPolicies', parameters('serverName'), parameters('name'))]" + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Web/sites/{0}/slots/{1}', parameters('appName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Web/sites/slots', parameters('appName'), 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": [ + "slot" + ] }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group of the deployed security alert policy." + "slot_appsettings": { + "condition": "[or(or(not(empty(parameters('appSettingsKeyValuePairs'))), not(empty(parameters('appInsightResourceId')))), not(empty(parameters('storageAccountResourceId'))))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Slot-{1}-Config-AppSettings', uniqueString(deployment().name, parameters('location')), parameters('name'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "slotName": { + "value": "[parameters('name')]" + }, + "appName": { + "value": "[parameters('appName')]" + }, + "kind": { + "value": "[parameters('kind')]" + }, + "storageAccountResourceId": { + "value": "[parameters('storageAccountResourceId')]" + }, + "storageAccountUseIdentityAuthentication": { + "value": "[parameters('storageAccountUseIdentityAuthentication')]" + }, + "appInsightResourceId": { + "value": "[parameters('appInsightResourceId')]" + }, + "appSettingsKeyValuePairs": { + "value": "[parameters('appSettingsKeyValuePairs')]" + }, + "currentAppSettings": "[if(not(empty(resourceId('Microsoft.Web/sites/slots', parameters('appName'), parameters('name')))), createObject('value', list(format('{0}/config/appsettings', resourceId('Microsoft.Web/sites/slots', parameters('appName'), parameters('name'))), '2023-12-01').properties), createObject('value', createObject()))]" + }, + "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.33.93.31351", + "templateHash": "18192409627790392598" + }, + "name": "Site Slot App Settings", + "description": "This module deploys a Site Slot App Setting." + }, + "parameters": { + "slotName": { + "type": "string", + "metadata": { + "description": "Required. Slot name to be configured." + } + }, + "appName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent site resource. Required if the template is used in a standalone deployment." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "functionapp", + "functionapp,linux", + "functionapp,workflowapp", + "functionapp,workflowapp,linux", + "functionapp,linux,container", + "functionapp,linux,container,azurecontainerapps", + "app,linux", + "app", + "linux,api", + "api", + "app,linux,container", + "app,container,windows" + ], + "metadata": { + "description": "Required. Type of site to deploy." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Required if app of kind functionapp. Resource ID of the storage account to manage triggers and logging function executions." + } + }, + "storageAccountUseIdentityAuthentication": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. If the provided storage account requires Identity based authentication ('allowSharedKeyAccess' is set to false). When set to true, the minimum role assignment required for the App Service Managed Identity to the storage account is 'Storage Blob Data Owner'." + } + }, + "appInsightResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the app insight to leverage for this resource." + } + }, + "appSettingsKeyValuePairs": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The app settings key-value pairs except for AzureWebJobsStorage, AzureWebJobsDashboard, APPINSIGHTS_INSTRUMENTATIONKEY and APPLICATIONINSIGHTS_CONNECTION_STRING." + } + }, + "currentAppSettings": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. The current app settings." + } + } + }, + "resources": { + "app::slot": { + "existing": true, + "type": "Microsoft.Web/sites/slots", + "apiVersion": "2024-04-01", + "name": "[format('{0}/{1}', parameters('appName'), parameters('slotName'))]" + }, + "app": { + "existing": true, + "type": "Microsoft.Web/sites", + "apiVersion": "2024-04-01", + "name": "[parameters('appName')]" + }, + "appInsight": { + "condition": "[not(empty(parameters('appInsightResourceId')))]", + "existing": true, + "type": "Microsoft.Insights/components", + "apiVersion": "2020-02-02", + "subscriptionId": "[split(parameters('appInsightResourceId'), '/')[2]]", + "resourceGroup": "[split(parameters('appInsightResourceId'), '/')[4]]", + "name": "[last(split(parameters('appInsightResourceId'), '/'))]" + }, + "storageAccount": { + "condition": "[not(empty(parameters('storageAccountResourceId')))]", + "existing": true, + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2023-05-01", + "subscriptionId": "[split(parameters('storageAccountResourceId'), '/')[2]]", + "resourceGroup": "[split(parameters('storageAccountResourceId'), '/')[4]]", + "name": "[last(split(parameters('storageAccountResourceId'), '/'))]" + }, + "slotSettings": { + "type": "Microsoft.Web/sites/slots/config", + "apiVersion": "2024-04-01", + "name": "[format('{0}/{1}/{2}', parameters('appName'), parameters('slotName'), 'appsettings')]", + "kind": "[parameters('kind')]", + "properties": "[union(coalesce(parameters('currentAppSettings'), createObject()), coalesce(parameters('appSettingsKeyValuePairs'), createObject()), if(and(not(empty(parameters('storageAccountResourceId'))), not(parameters('storageAccountUseIdentityAuthentication'))), createObject('AzureWebJobsStorage', format('DefaultEndpointsProtocol=https;AccountName={0};AccountKey={1};EndpointSuffix={2}', last(split(parameters('storageAccountResourceId'), '/')), listKeys(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('storageAccountResourceId'), '/')[2], split(parameters('storageAccountResourceId'), '/')[4]), 'Microsoft.Storage/storageAccounts', last(split(parameters('storageAccountResourceId'), '/'))), '2023-05-01').keys[0].value, environment().suffixes.storage)), if(and(not(empty(parameters('storageAccountResourceId'))), parameters('storageAccountUseIdentityAuthentication')), union(createObject('AzureWebJobsStorage__accountName', last(split(parameters('storageAccountResourceId'), '/'))), createObject('AzureWebJobsStorage__blobServiceUri', reference('storageAccount').primaryEndpoints.blob)), createObject())), if(not(empty(parameters('appInsightResourceId'))), createObject('APPLICATIONINSIGHTS_CONNECTION_STRING', reference('appInsight').ConnectionString), createObject()))]", + "dependsOn": [ + "appInsight", + "storageAccount" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the slot config." + }, + "value": "appsettings" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the slot config." + }, + "value": "[resourceId('Microsoft.Web/sites/slots/config', parameters('appName'), parameters('slotName'), 'appsettings')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the slot config was deployed into." + }, + "value": "[resourceGroup().name]" + } + } + } }, - "value": "[resourceGroup().name]" - } - } - } - }, - "dependsOn": [ - "server" - ] - }, - "server_vulnerabilityAssessment": { - "condition": "[not(equals(parameters('vulnerabilityAssessmentsObj'), null()))]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-Sql-VulnAssessm', uniqueString(deployment().name, parameters('location')))]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "serverName": { - "value": "[parameters('name')]" - }, - "name": { - "value": "[parameters('vulnerabilityAssessmentsObj').name]" - }, - "recurringScans": { - "value": "[tryGet(parameters('vulnerabilityAssessmentsObj'), 'recurringScans')]" - }, - "storageAccountResourceId": { - "value": "[parameters('vulnerabilityAssessmentsObj').storageAccountResourceId]" - }, - "useStorageAccountAccessKey": { - "value": "[tryGet(parameters('vulnerabilityAssessmentsObj'), 'useStorageAccountAccessKey')]" - }, - "createStorageRoleAssignment": { - "value": "[tryGet(parameters('vulnerabilityAssessmentsObj'), 'createStorageRoleAssignment')]" - } - }, - "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.34.44.8038", - "templateHash": "16010734059576789187" + "dependsOn": [ + "slot" + ] + }, + "slot_authsettingsv2": { + "condition": "[not(empty(parameters('authSettingV2Configuration')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Slot-{1}-Config-AuthSettingsV2', uniqueString(deployment().name, parameters('location')), parameters('name'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "slotName": { + "value": "[parameters('name')]" + }, + "appName": { + "value": "[parameters('appName')]" + }, + "kind": { + "value": "[parameters('kind')]" + }, + "authSettingV2Configuration": { + "value": "[coalesce(parameters('authSettingV2Configuration'), createObject())]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.33.93.31351", + "templateHash": "4602741618711602070" + }, + "name": "Site Slot Auth Settings V2 Config", + "description": "This module deploys a Site Auth Settings V2 Configuration." + }, + "parameters": { + "appName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent site resource. Required if the template is used in a standalone deployment." + } + }, + "slotName": { + "type": "string", + "metadata": { + "description": "Required. Slot name to be configured." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "functionapp", + "functionapp,linux", + "functionapp,workflowapp", + "functionapp,workflowapp,linux", + "functionapp,linux,container", + "functionapp,linux,container,azurecontainerapps", + "app,linux", + "app", + "linux,api", + "api", + "app,linux,container", + "app,container,windows" + ], + "metadata": { + "description": "Required. Type of site to deploy." + } + }, + "authSettingV2Configuration": { + "type": "object", + "metadata": { + "description": "Required. The auth settings V2 configuration." + } + } + }, + "resources": [ + { + "type": "Microsoft.Web/sites/slots/config", + "apiVersion": "2024-04-01", + "name": "[format('{0}/{1}/{2}', parameters('appName'), parameters('slotName'), 'authsettingsV2')]", + "kind": "[parameters('kind')]", + "properties": "[parameters('authSettingV2Configuration')]" + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the slot config." + }, + "value": "authsettingsV2" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the slot config." + }, + "value": "[resourceId('Microsoft.Web/sites/slots/config', parameters('appName'), parameters('slotName'), 'authsettingsV2')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the slot config was deployed into." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "slot" + ] + }, + "slot_basicPublishingCredentialsPolicies": { + "copy": { + "name": "slot_basicPublishingCredentialsPolicies", + "count": "[length(coalesce(parameters('basicPublishingCredentialsPolicies'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Slot-Publish-Cred-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "appName": { + "value": "[parameters('appName')]" + }, + "slotName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('basicPublishingCredentialsPolicies'), createArray())[copyIndex()].name]" + }, + "allow": { + "value": "[tryGet(coalesce(parameters('basicPublishingCredentialsPolicies'), createArray())[copyIndex()], 'allow')]" + }, + "location": { + "value": "[parameters('location')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.33.93.31351", + "templateHash": "8803130402255189673" + }, + "name": "Web Site Slot Basic Publishing Credentials Policies", + "description": "This module deploys a Web Site Slot Basic Publishing Credentials Policy." + }, + "parameters": { + "name": { + "type": "string", + "allowedValues": [ + "scm", + "ftp" + ], + "metadata": { + "description": "Required. The name of the resource." + } + }, + "allow": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Set to true to enable or false to disable a publishing method." + } + }, + "appName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent web site. Required if the template is used in a standalone deployment." + } + }, + "slotName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent web site slot. Required if the template is used in a standalone deployment." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all Resources." + } + } + }, + "resources": [ + { + "type": "Microsoft.Web/sites/slots/basicPublishingCredentialsPolicies", + "apiVersion": "2024-04-01", + "name": "[format('{0}/{1}/{2}', parameters('appName'), parameters('slotName'), parameters('name'))]", + "location": "[parameters('location')]", + "properties": { + "allow": "[parameters('allow')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the basic publishing credential policy." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the basic publishing credential policy." + }, + "value": "[resourceId('Microsoft.Web/sites/slots/basicPublishingCredentialsPolicies', parameters('appName'), parameters('slotName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the basic publishing credential policy was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference(resourceId('Microsoft.Web/sites/slots/basicPublishingCredentialsPolicies', parameters('appName'), parameters('slotName'), parameters('name')), '2024-04-01', 'full').location]" + } + } + } + }, + "dependsOn": [ + "slot" + ] }, - "name": "Azure SQL Server Vulnerability Assessments", - "description": "This module deploys an Azure SQL Server Vulnerability Assessment." - }, - "definitions": { - "recurringScansType": { - "type": "object", + "slot_hybridConnectionRelays": { + "copy": { + "name": "slot_hybridConnectionRelays", + "count": "[length(coalesce(parameters('hybridConnectionRelays'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Slot-HybridConnectionRelay-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { - "emails": { - "type": "array", - "items": { - "type": "string" + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "hybridConnectionResourceId": { + "value": "[coalesce(parameters('hybridConnectionRelays'), createArray())[copyIndex()].resourceId]" }, - "metadata": { - "description": "Required. Specifies an array of e-mail addresses to which the scan notification is sent." + "appName": { + "value": "[parameters('appName')]" + }, + "slotName": { + "value": "[parameters('name')]" + }, + "sendKeyName": { + "value": "[tryGet(coalesce(parameters('hybridConnectionRelays'), createArray())[copyIndex()], 'sendKeyName')]" } }, - "emailSubscriptionAdmins": { - "type": "bool", - "nullable": true, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", "metadata": { - "description": "Optional. Specifies that the schedule scan notification will be sent to the subscription administrators." + "_generator": { + "name": "bicep", + "version": "0.33.93.31351", + "templateHash": "16445776675656358479" + }, + "name": "Web/Function Apps Slot Hybrid Connection Relay", + "description": "This module deploys a Site Slot Hybrid Connection Namespace Relay." + }, + "parameters": { + "hybridConnectionResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the relay namespace hybrid connection." + } + }, + "slotName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the site slot. Required if the template is used in a standalone deployment." + } + }, + "appName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent web site. Required if the template is used in a standalone deployment." + } + }, + "sendKeyName": { + "type": "string", + "defaultValue": "defaultSender", + "metadata": { + "description": "Optional. Name of the authorization rule send key to use." + } + } + }, + "resources": [ + { + "type": "Microsoft.Web/sites/slots/hybridConnectionNamespaces/relays", + "apiVersion": "2024-04-01", + "name": "[format('{0}/{1}/{2}/{3}', parameters('appName'), parameters('slotName'), split(parameters('hybridConnectionResourceId'), '/')[8], split(parameters('hybridConnectionResourceId'), '/')[10])]", + "properties": { + "serviceBusNamespace": "[split(parameters('hybridConnectionResourceId'), '/')[8]]", + "serviceBusSuffix": "[split(substring(reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('hybridConnectionResourceId'), '/')[2], split(parameters('hybridConnectionResourceId'), '/')[4]), 'Microsoft.Relay/namespaces', split(parameters('hybridConnectionResourceId'), '/')[8]), '2021-11-01').serviceBusEndpoint, indexOf(reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('hybridConnectionResourceId'), '/')[2], split(parameters('hybridConnectionResourceId'), '/')[4]), 'Microsoft.Relay/namespaces', split(parameters('hybridConnectionResourceId'), '/')[8]), '2021-11-01').serviceBusEndpoint, '.servicebus')), ':')[0]]", + "relayName": "[split(parameters('hybridConnectionResourceId'), '/')[10]]", + "relayArmUri": "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('hybridConnectionResourceId'), '/')[2], split(parameters('hybridConnectionResourceId'), '/')[4]), 'Microsoft.Relay/namespaces/hybridConnections', split(parameters('hybridConnectionResourceId'), '/')[8], split(parameters('hybridConnectionResourceId'), '/')[10])]", + "hostname": "[split(json(reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('hybridConnectionResourceId'), '/')[2], split(parameters('hybridConnectionResourceId'), '/')[4]), 'Microsoft.Relay/namespaces/hybridConnections', split(parameters('hybridConnectionResourceId'), '/')[8], split(parameters('hybridConnectionResourceId'), '/')[10]), '2021-11-01').userMetadata)[0].value, ':')[0]]", + "port": "[int(split(json(reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('hybridConnectionResourceId'), '/')[2], split(parameters('hybridConnectionResourceId'), '/')[4]), 'Microsoft.Relay/namespaces/hybridConnections', split(parameters('hybridConnectionResourceId'), '/')[8], split(parameters('hybridConnectionResourceId'), '/')[10]), '2021-11-01').userMetadata)[0].value, ':')[1])]", + "sendKeyName": "[parameters('sendKeyName')]", + "sendKeyValue": "[listKeys(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('hybridConnectionResourceId'), '/')[2], split(parameters('hybridConnectionResourceId'), '/')[4]), 'Microsoft.Relay/namespaces/hybridConnections/authorizationRules', split(parameters('hybridConnectionResourceId'), '/')[8], split(parameters('hybridConnectionResourceId'), '/')[10], parameters('sendKeyName')), '2021-11-01').primaryKey]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the hybrid connection relay.." + }, + "value": "[format('{0}/{1}/{2}/{3}', parameters('appName'), parameters('slotName'), split(parameters('hybridConnectionResourceId'), '/')[8], split(parameters('hybridConnectionResourceId'), '/')[10])]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the hybrid connection relay." + }, + "value": "[resourceId('Microsoft.Web/sites/slots/hybridConnectionNamespaces/relays', split(format('{0}/{1}/{2}/{3}', parameters('appName'), parameters('slotName'), split(parameters('hybridConnectionResourceId'), '/')[8], split(parameters('hybridConnectionResourceId'), '/')[10]), '/')[0], split(format('{0}/{1}/{2}/{3}', parameters('appName'), parameters('slotName'), split(parameters('hybridConnectionResourceId'), '/')[8], split(parameters('hybridConnectionResourceId'), '/')[10]), '/')[1], split(format('{0}/{1}/{2}/{3}', parameters('appName'), parameters('slotName'), split(parameters('hybridConnectionResourceId'), '/')[8], split(parameters('hybridConnectionResourceId'), '/')[10]), '/')[2], split(format('{0}/{1}/{2}/{3}', parameters('appName'), parameters('slotName'), split(parameters('hybridConnectionResourceId'), '/')[8], split(parameters('hybridConnectionResourceId'), '/')[10]), '/')[3])]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the resource was deployed into." + }, + "value": "[resourceGroup().name]" + } } + } + }, + "dependsOn": [ + "slot" + ] + }, + "slot_extensionMSdeploy": { + "condition": "[not(empty(parameters('msDeployConfiguration')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Site-Extension-MSDeploy', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" }, - "isEnabled": { - "type": "bool", - "nullable": true, + "mode": "Incremental", + "parameters": { + "appName": { + "value": "[parameters('appName')]" + }, + "msDeployConfiguration": { + "value": "[coalesce(parameters('msDeployConfiguration'), createObject())]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", "metadata": { - "description": "Optional. Recurring scans state." + "_generator": { + "name": "bicep", + "version": "0.33.93.31351", + "templateHash": "14895622660217616811" + }, + "name": "Site Deployment Extension ", + "description": "This module deploys a Site extension for MSDeploy." + }, + "parameters": { + "appName": { + "type": "string", + "metadata": { + "description": "Required. The name of the parent site resource." + } + }, + "msDeployConfiguration": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Sets the MSDeployment Properties." + } + } + }, + "resources": { + "app": { + "existing": true, + "type": "Microsoft.Web/sites", + "apiVersion": "2024-04-01", + "name": "[parameters('appName')]" + }, + "msdeploy": { + "type": "Microsoft.Web/sites/extensions", + "apiVersion": "2024-04-01", + "name": "[format('{0}/{1}', parameters('appName'), 'MSDeploy')]", + "kind": "MSDeploy", + "properties": "[parameters('msDeployConfiguration')]" + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the MSDeploy Package." + }, + "value": "MSDeploy" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the Site Extension." + }, + "value": "[resourceId('Microsoft.Web/sites/extensions', parameters('appName'), 'MSDeploy')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the site config was deployed into." + }, + "value": "[resourceGroup().name]" + } } } - }, - "metadata": { - "__bicep_export!": true - } - } - }, - "parameters": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the vulnerability assessment." - } - }, - "serverName": { - "type": "string", - "metadata": { - "description": "Conditional. The Name of SQL Server. Required if the template is used in a standalone deployment." } }, - "recurringScans": { - "$ref": "#/definitions/recurringScansType", - "defaultValue": { - "emails": [], - "emailSubscriptionAdmins": false, - "isEnabled": false + "slot_privateEndpoints": { + "copy": { + "name": "slot_privateEndpoints", + "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]" }, - "metadata": { - "description": "Optional. The recurring scans settings." - } - }, - "storageAccountResourceId": { - "type": "string", - "metadata": { - "description": "Required. A blob storage to hold the scan results." - } - }, - "useStorageAccountAccessKey": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "Optional. Use Access Key to access the storage account. The storage account cannot be behind a firewall or virtual network. If an access key is not used, the SQL Server system assigned managed identity must be assigned the Storage Blob Data Contributor role on the storage account." - } - }, - "createStorageRoleAssignment": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Optional. Create the Storage Blob Data Contributor role assignment on the storage account. Note, the role assignment must not already exist on the storage account." - } - } - }, - "resources": { - "server": { - "existing": true, - "type": "Microsoft.Sql/servers", - "apiVersion": "2023-08-01-preview", - "name": "[parameters('serverName')]" - }, - "vulnerabilityAssessment": { - "type": "Microsoft.Sql/servers/vulnerabilityAssessments", - "apiVersion": "2023-08-01-preview", - "name": "[format('{0}/{1}', parameters('serverName'), parameters('name'))]", - "properties": { - "storageContainerPath": "[format('https://{0}.blob.{1}/vulnerability-assessment/', last(split(parameters('storageAccountResourceId'), '/')), environment().suffixes.storage)]", - "storageAccountAccessKey": "[if(parameters('useStorageAccountAccessKey'), listKeys(parameters('storageAccountResourceId'), '2019-06-01').keys[0].value, null())]", - "recurringScans": "[parameters('recurringScans')]" - } - }, - "storageAccount_sbdc_rbac": { - "condition": "[and(not(parameters('useStorageAccountAccessKey')), parameters('createStorageRoleAssignment'))]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-sbdc-rbac', parameters('serverName'))]", - "subscriptionId": "[split(parameters('storageAccountResourceId'), '/')[2]]", - "resourceGroup": "[split(parameters('storageAccountResourceId'), '/')[4]]", + "name": "[format('{0}-slot-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]]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "storageAccountName": { - "value": "[last(split(parameters('storageAccountResourceId'), '/'))]" + "name": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'name'), format('pep-{0}-{1}-{2}', last(split(resourceId('Microsoft.Web/sites', parameters('appName')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), format('sites-{0}', parameters('name'))), copyIndex()))]" }, - "managedInstanceIdentityPrincipalId": { - "value": "[reference('server', '2023-08-01-preview', 'full').identity.principalId]" + "privateLinkServiceConnections": "[if(not(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true())), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.Web/sites', parameters('appName')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), format('sites-{0}', parameters('name'))), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.Web/sites', parameters('appName')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), format('sites-{0}', parameters('name')))))))), createObject('value', null()))]", + "manualPrivateLinkServiceConnections": "[if(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true()), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.Web/sites', parameters('appName')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), format('sites-{0}', parameters('name'))), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.Web/sites', parameters('appName')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), format('sites-{0}', parameters('name')))), 'requestMessage', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualConnectionRequestMessage'), 'Manual approval required.'))))), createObject('value', null()))]", + "subnetResourceId": { + "value": "[coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId]" + }, + "enableTelemetry": { + "value": "[variables('enableReferencedModulesTelemetry')]" + }, + "location": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'location'), reference(split(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location)]" + }, + "lock": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" + }, + "privateDnsZoneGroup": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroup')]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'roleAssignments')]" + }, + "tags": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + }, + "customDnsConfigs": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customDnsConfigs')]" + }, + "ipConfigurations": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'ipConfigurations')]" + }, + "applicationSecurityGroupResourceIds": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'applicationSecurityGroupResourceIds')]" + }, + "customNetworkInterfaceName": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customNetworkInterfaceName')]" } }, "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.34.44.8038", - "templateHash": "16931034564891209519" + "version": "0.33.13.18514", + "templateHash": "15954548978129725136" + }, + "name": "Private Endpoints", + "description": "This module deploys a Private Endpoint." + }, + "definitions": { + "privateDnsZoneGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, + "metadata": { + "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "ipConfigurationType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource that is unique within a resource group." + } + }, + "properties": { + "type": "object", + "properties": { + "groupId": { + "type": "string", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } + } + }, + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "privateLinkServiceConnectionType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the private link service connection." + } + }, + "properties": { + "type": "object", + "properties": { + "groupIds": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." + } + }, + "privateLinkServiceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of private link service." + } + }, + "requestMessage": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." + } + } + }, + "metadata": { + "description": "Required. Properties of private link service connection." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "customDnsConfigType": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. FQDN that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "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." + } + } + }, + "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.5.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": { + "__bicep_imported_from!": { + "sourceTemplate": "private-dns-zone-group/main.bicep" + } + } + }, + "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.5.1" + } + } } }, "parameters": { - "storageAccountName": { - "type": "string" + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the private endpoint resource to create." + } }, - "managedInstanceIdentityPrincipalId": { - "type": "string" + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + } + }, + "applicationSecurityGroupResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Application security groups in which the private endpoint IP configuration is included." + } + }, + "customNetworkInterfaceName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The custom name of the network interface attached to the private endpoint." + } + }, + "ipConfigurations": { + "type": "array", + "items": { + "$ref": "#/definitions/ipConfigurationType" + }, + "nullable": true, + "metadata": { + "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." + } + }, + "privateDnsZoneGroup": { + "$ref": "#/definitions/privateDnsZoneGroupType", + "nullable": true, + "metadata": { + "description": "Optional. The private DNS zone group to configure for the private endpoint." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "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", + "nullable": true, + "metadata": { + "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/customDnsConfigType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Custom DNS configurations." + } + }, + "manualPrivateLinkServiceConnections": { + "type": "array", + "items": { + "$ref": "#/definitions/privateLinkServiceConnectionType" + }, + "nullable": true, + "metadata": { + "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." + } + }, + "privateLinkServiceConnections": { + "type": "array", + "items": { + "$ref": "#/definitions/privateLinkServiceConnectionType" + }, + "nullable": true, + "metadata": { + "description": "Conditional. A grouping of information about the connection to the remote resource. Required if `manualPrivateLinkServiceConnections` is empty." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } } }, - "resources": [ - { + "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": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.10.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": "2023-11-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": "[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.Storage/storageAccounts/{0}', parameters('storageAccountName'))]", - "name": "[guid(format('{0}-{1}-Storage-Blob-Data-Contributor', resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName')), parameters('managedInstanceIdentityPrincipalId')))]", + "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": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe')]", - "principalId": "[parameters('managedInstanceIdentityPrincipalId')]", - "principalType": "ServicePrincipal" - } + "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": "2022-09-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.33.13.18514", + "templateHash": "5440815542537978381" + }, + "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 + } + } + }, + "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." + } + } + }, + "variables": { + "copy": [ + { + "name": "privateDnsZoneConfigsVar", + "count": "[length(parameters('privateDnsZoneConfigs'))]", + "input": { + "name": "[coalesce(tryGet(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')], 'name'), last(split(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId, '/')))]", + "properties": { + "privateDnsZoneId": "[parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId]" + } + } + } + ] + }, + "resources": { + "privateEndpoint": { + "existing": true, + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-11-01", + "name": "[parameters('privateEndpointName')]" + }, + "privateDnsZoneGroup": { + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2023-11-01", + "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", + "properties": { + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" + } + } + }, + "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" + ] } - ] - } - }, - "dependsOn": [ - "server" - ] - } - }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the deployed vulnerability assessment." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the deployed vulnerability assessment." - }, - "value": "[resourceId('Microsoft.Sql/servers/vulnerabilityAssessments', parameters('serverName'), parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group of the deployed vulnerability assessment." - }, - "value": "[resourceGroup().name]" - } - } - } - }, - "dependsOn": [ - "server", - "server_securityAlertPolicies" - ] - }, - "server_keys": { - "copy": { - "name": "server_keys", - "count": "[length(parameters('keys'))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-Sql-Key-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "serverName": { - "value": "[parameters('name')]" - }, - "name": { - "value": "[tryGet(parameters('keys')[copyIndex()], 'name')]" - }, - "serverKeyType": { - "value": "[tryGet(parameters('keys')[copyIndex()], 'serverKeyType')]" - }, - "uri": { - "value": "[tryGet(parameters('keys')[copyIndex()], 'uri')]" - } - }, - "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.34.44.8038", - "templateHash": "9797257758598562244" - }, - "name": "Azure SQL Server Keys", - "description": "This module deploys an Azure SQL Server Key." + }, + "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', '2023-11-01', 'full').location]" + }, + "customDnsConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/customDnsConfigType" + }, + "metadata": { + "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": [ + "slot" + ] + } }, - "parameters": { + "outputs": { "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the key. Must follow the [__] pattern." - } - }, - "serverName": { "type": "string", "metadata": { - "description": "Conditional. The name of the parent SQL server. Required if the template is used in a standalone deployment." - } + "description": "The name of the slot." + }, + "value": "[parameters('name')]" }, - "serverKeyType": { + "resourceId": { "type": "string", - "defaultValue": "ServiceManaged", - "allowedValues": [ - "AzureKeyVault", - "ServiceManaged" - ], "metadata": { - "description": "Optional. The server key type." - } + "description": "The resource ID of the slot." + }, + "value": "[resourceId('Microsoft.Web/sites/slots', parameters('appName'), parameters('name'))]" }, - "uri": { + "resourceGroupName": { "type": "string", - "defaultValue": "", "metadata": { - "description": "Optional. The URI of the server key. If the ServerKeyType is AzureKeyVault, then the URI is required. The AKV URI is required to be in this format: 'https://YourVaultName.azure.net/keys/YourKeyName/YourKeyVersion'." - } - } - }, - "variables": { - "splittedKeyUri": "[split(parameters('uri'), '/')]", - "serverKeyName": "[if(empty(parameters('uri')), 'ServiceManaged', format('{0}_{1}_{2}', split(variables('splittedKeyUri')[2], '.')[0], variables('splittedKeyUri')[4], variables('splittedKeyUri')[5]))]" - }, - "resources": { - "server": { - "existing": true, - "type": "Microsoft.Sql/servers", - "apiVersion": "2023-08-01-preview", - "name": "[parameters('serverName')]" + "description": "The resource group the slot was deployed into." + }, + "value": "[resourceGroup().name]" }, - "key": { - "type": "Microsoft.Sql/servers/keys", - "apiVersion": "2023-08-01-preview", - "name": "[format('{0}/{1}', parameters('serverName'), coalesce(parameters('name'), variables('serverKeyName')))]", - "properties": { - "serverKeyType": "[parameters('serverKeyType')]", - "uri": "[parameters('uri')]" - } - } - }, - "outputs": { - "name": { + "systemAssignedMIPrincipalId": { "type": "string", + "nullable": true, "metadata": { - "description": "The name of the deployed server key." + "description": "The principal ID of the system assigned identity." }, - "value": "[coalesce(parameters('name'), variables('serverKeyName'))]" + "value": "[tryGet(tryGet(reference('slot', '2024-04-01', 'full'), 'identity'), 'principalId')]" }, - "resourceId": { + "location": { "type": "string", "metadata": { - "description": "The resource ID of the deployed server key." + "description": "The location the resource was deployed into." }, - "value": "[resourceId('Microsoft.Sql/servers/keys', parameters('serverName'), coalesce(parameters('name'), variables('serverKeyName')))]" + "value": "[reference('slot', '2024-04-01', 'full').location]" }, - "resourceGroupName": { - "type": "string", + "privateEndpoints": { + "type": "array", + "items": { + "$ref": "#/definitions/privateEndpointOutputType" + }, "metadata": { - "description": "The resource group of the deployed server key." + "description": "The private endpoints of the slot." }, - "value": "[resourceGroup().name]" + "copy": { + "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]", + "input": { + "name": "[reference(format('slot_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", + "resourceId": "[reference(format('slot_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", + "groupId": "[tryGet(tryGet(reference(format('slot_privateEndpoints[{0}]', copyIndex())).outputs, 'groupId'), 'value')]", + "customDnsConfigs": "[reference(format('slot_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfigs.value]", + "networkInterfaceResourceIds": "[reference(format('slot_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceResourceIds.value]" + } + } } } } }, "dependsOn": [ - "server" + "app" ] }, - "cmk_key": { - "condition": "[not(equals(parameters('customerManagedKey'), null()))]", + "app_basicPublishingCredentialsPolicies": { + "copy": { + "name": "app_basicPublishingCredentialsPolicies", + "count": "[length(coalesce(parameters('basicPublishingCredentialsPolicies'), createArray()))]" + }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-Sql-Key', uniqueString(deployment().name, parameters('location')))]", + "name": "[format('{0}-Site-Publish-Cred-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "serverName": { + "webAppName": { "value": "[parameters('name')]" }, "name": { - "value": "[format('{0}_{1}_{2}', last(split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')), tryGet(parameters('customerManagedKey'), 'keyName'), tryGet(parameters('customerManagedKey'), 'keyVersion'))]" + "value": "[coalesce(parameters('basicPublishingCredentialsPolicies'), createArray())[copyIndex()].name]" }, - "serverKeyType": { - "value": "AzureKeyVault" + "allow": { + "value": "[tryGet(coalesce(parameters('basicPublishingCredentialsPolicies'), createArray())[copyIndex()], 'allow')]" }, - "uri": "[if(not(empty(tryGet(parameters('customerManagedKey'), 'keyVersion'))), createObject('value', format('{0}/{1}', reference('cMKKeyVault::cMKKey').keyUri, tryGet(parameters('customerManagedKey'), 'keyVersion'))), if(coalesce(tryGet(parameters('customerManagedKey'), 'autoRotationEnabled'), true()), createObject('value', reference('cMKKeyVault::cMKKey').keyUri), createObject('value', reference('cMKKeyVault::cMKKey').keyUriWithVersion)))]" + "location": { + "value": "[parameters('location')]" + } }, "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.34.44.8038", - "templateHash": "9797257758598562244" + "version": "0.33.93.31351", + "templateHash": "7001118912896436334" }, - "name": "Azure SQL Server Keys", - "description": "This module deploys an Azure SQL Server Key." + "name": "Web Site Basic Publishing Credentials Policies", + "description": "This module deploys a Web Site Basic Publishing Credentials Policy." }, "parameters": { "name": { "type": "string", - "nullable": true, + "allowedValues": [ + "scm", + "ftp" + ], "metadata": { - "description": "Optional. The name of the key. Must follow the [__] pattern." + "description": "Required. The name of the resource." } }, - "serverName": { - "type": "string", + "allow": { + "type": "bool", + "defaultValue": true, "metadata": { - "description": "Conditional. The name of the parent SQL server. Required if the template is used in a standalone deployment." + "description": "Optional. Set to true to enable or false to disable a publishing method." } }, - "serverKeyType": { + "webAppName": { "type": "string", - "defaultValue": "ServiceManaged", - "allowedValues": [ - "AzureKeyVault", - "ServiceManaged" - ], "metadata": { - "description": "Optional. The server key type." + "description": "Conditional. The name of the parent web site. Required if the template is used in a standalone deployment." } }, - "uri": { + "location": { "type": "string", - "defaultValue": "", + "defaultValue": "[resourceGroup().location]", "metadata": { - "description": "Optional. The URI of the server key. If the ServerKeyType is AzureKeyVault, then the URI is required. The AKV URI is required to be in this format: 'https://YourVaultName.azure.net/keys/YourKeyName/YourKeyVersion'." + "description": "Optional. Location for all Resources." } } }, - "variables": { - "splittedKeyUri": "[split(parameters('uri'), '/')]", - "serverKeyName": "[if(empty(parameters('uri')), 'ServiceManaged', format('{0}_{1}_{2}', split(variables('splittedKeyUri')[2], '.')[0], variables('splittedKeyUri')[4], variables('splittedKeyUri')[5]))]" - }, - "resources": { - "server": { - "existing": true, - "type": "Microsoft.Sql/servers", - "apiVersion": "2023-08-01-preview", - "name": "[parameters('serverName')]" - }, - "key": { - "type": "Microsoft.Sql/servers/keys", - "apiVersion": "2023-08-01-preview", - "name": "[format('{0}/{1}', parameters('serverName'), coalesce(parameters('name'), variables('serverKeyName')))]", + "resources": [ + { + "type": "Microsoft.Web/sites/basicPublishingCredentialsPolicies", + "apiVersion": "2024-04-01", + "name": "[format('{0}/{1}', parameters('webAppName'), parameters('name'))]", + "location": "[parameters('location')]", "properties": { - "serverKeyType": "[parameters('serverKeyType')]", - "uri": "[parameters('uri')]" + "allow": "[parameters('allow')]" } } - }, + ], "outputs": { "name": { "type": "string", "metadata": { - "description": "The name of the deployed server key." + "description": "The name of the basic publishing credential policy." }, - "value": "[coalesce(parameters('name'), variables('serverKeyName'))]" + "value": "[parameters('name')]" }, "resourceId": { "type": "string", "metadata": { - "description": "The resource ID of the deployed server key." + "description": "The resource ID of the basic publishing credential policy." }, - "value": "[resourceId('Microsoft.Sql/servers/keys', parameters('serverName'), coalesce(parameters('name'), variables('serverKeyName')))]" + "value": "[resourceId('Microsoft.Web/sites/basicPublishingCredentialsPolicies', parameters('webAppName'), parameters('name'))]" }, "resourceGroupName": { "type": "string", "metadata": { - "description": "The resource group of the deployed server key." + "description": "The name of the resource group the basic publishing credential policy was deployed into." }, "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference(resourceId('Microsoft.Web/sites/basicPublishingCredentialsPolicies', parameters('webAppName'), parameters('name')), '2024-04-01', 'full').location]" } } } }, "dependsOn": [ - "cMKKeyVault::cMKKey", - "server" + "app" ] }, - "server_encryptionProtector": { - "condition": "[not(equals(parameters('customerManagedKey'), null()))]", + "app_hybridConnectionRelays": { + "copy": { + "name": "app_hybridConnectionRelays", + "count": "[length(coalesce(parameters('hybridConnectionRelays'), createArray()))]" + }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-Sql-EncryProtector', uniqueString(deployment().name, parameters('location')))]", + "name": "[format('{0}-HybridConnectionRelay-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "sqlServerName": { - "value": "[parameters('name')]" - }, - "serverKeyName": { - "value": "[reference('cmk_key').outputs.name.value]" + "hybridConnectionResourceId": { + "value": "[coalesce(parameters('hybridConnectionRelays'), createArray())[copyIndex()].resourceId]" }, - "serverKeyType": { - "value": "AzureKeyVault" + "appName": { + "value": "[parameters('name')]" }, - "autoRotationEnabled": { - "value": "[tryGet(parameters('customerManagedKey'), 'autoRotationEnabled')]" + "sendKeyName": { + "value": "[tryGet(coalesce(parameters('hybridConnectionRelays'), createArray())[copyIndex()], 'sendKeyName')]" } }, "template": { @@ -86292,53 +98119,47 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.34.44.8038", - "templateHash": "13156357596009101804" + "version": "0.33.93.31351", + "templateHash": "13214417392638890300" }, - "name": "Azure SQL Server Encryption Protector", - "description": "This module deploys an Azure SQL Server Encryption Protector." + "name": "Web/Function Apps Hybrid Connection Relay", + "description": "This module deploys a Site Hybrid Connection Namespace Relay." }, "parameters": { - "sqlServerName": { + "hybridConnectionResourceId": { "type": "string", "metadata": { - "description": "Conditional. The name of the sql server. Required if the template is used in a standalone deployment." + "description": "Required. The resource ID of the relay namespace hybrid connection." } }, - "serverKeyName": { + "appName": { "type": "string", "metadata": { - "description": "Required. The name of the server key." - } - }, - "autoRotationEnabled": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Optional. Key auto rotation opt-in flag." + "description": "Conditional. The name of the parent web site. Required if the template is used in a standalone deployment." } }, - "serverKeyType": { + "sendKeyName": { "type": "string", - "defaultValue": "ServiceManaged", - "allowedValues": [ - "AzureKeyVault", - "ServiceManaged" - ], + "defaultValue": "defaultSender", "metadata": { - "description": "Optional. The encryption protector type." + "description": "Optional. Name of the authorization rule send key to use." } } }, "resources": [ { - "type": "Microsoft.Sql/servers/encryptionProtector", - "apiVersion": "2023-08-01-preview", - "name": "[format('{0}/{1}', parameters('sqlServerName'), 'current')]", + "type": "Microsoft.Web/sites/hybridConnectionNamespaces/relays", + "apiVersion": "2024-04-01", + "name": "[format('{0}/{1}/{2}', parameters('appName'), split(parameters('hybridConnectionResourceId'), '/')[8], split(parameters('hybridConnectionResourceId'), '/')[10])]", "properties": { - "serverKeyType": "[parameters('serverKeyType')]", - "autoRotationEnabled": "[parameters('autoRotationEnabled')]", - "serverKeyName": "[parameters('serverKeyName')]" + "serviceBusNamespace": "[split(parameters('hybridConnectionResourceId'), '/')[8]]", + "serviceBusSuffix": "[split(substring(reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('hybridConnectionResourceId'), '/')[2], split(parameters('hybridConnectionResourceId'), '/')[4]), 'Microsoft.Relay/namespaces', split(parameters('hybridConnectionResourceId'), '/')[8]), '2021-11-01').serviceBusEndpoint, indexOf(reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('hybridConnectionResourceId'), '/')[2], split(parameters('hybridConnectionResourceId'), '/')[4]), 'Microsoft.Relay/namespaces', split(parameters('hybridConnectionResourceId'), '/')[8]), '2021-11-01').serviceBusEndpoint, '.servicebus')), ':')[0]]", + "relayName": "[split(parameters('hybridConnectionResourceId'), '/')[10]]", + "relayArmUri": "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('hybridConnectionResourceId'), '/')[2], split(parameters('hybridConnectionResourceId'), '/')[4]), 'Microsoft.Relay/namespaces/hybridConnections', split(parameters('hybridConnectionResourceId'), '/')[8], split(parameters('hybridConnectionResourceId'), '/')[10])]", + "hostname": "[split(json(reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('hybridConnectionResourceId'), '/')[2], split(parameters('hybridConnectionResourceId'), '/')[4]), 'Microsoft.Relay/namespaces/hybridConnections', split(parameters('hybridConnectionResourceId'), '/')[8], split(parameters('hybridConnectionResourceId'), '/')[10]), '2021-11-01').userMetadata)[0].value, ':')[0]]", + "port": "[int(split(json(reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('hybridConnectionResourceId'), '/')[2], split(parameters('hybridConnectionResourceId'), '/')[4]), 'Microsoft.Relay/namespaces/hybridConnections', split(parameters('hybridConnectionResourceId'), '/')[8], split(parameters('hybridConnectionResourceId'), '/')[10]), '2021-11-01').userMetadata)[0].value, ':')[1])]", + "sendKeyName": "[parameters('sendKeyName')]", + "sendKeyValue": "[listKeys(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('hybridConnectionResourceId'), '/')[2], split(parameters('hybridConnectionResourceId'), '/')[4]), 'Microsoft.Relay/namespaces/hybridConnections/authorizationRules', split(parameters('hybridConnectionResourceId'), '/')[8], split(parameters('hybridConnectionResourceId'), '/')[10], parameters('sendKeyName')), '2021-11-01').primaryKey]" } } ], @@ -86346,21 +98167,21 @@ "name": { "type": "string", "metadata": { - "description": "The name of the deployed encryption protector." + "description": "The name of the hybrid connection relay.." }, - "value": "current" + "value": "[format('{0}/{1}/{2}', parameters('appName'), split(parameters('hybridConnectionResourceId'), '/')[8], split(parameters('hybridConnectionResourceId'), '/')[10])]" }, "resourceId": { "type": "string", "metadata": { - "description": "The resource ID of the encryption protector." + "description": "The resource ID of the hybrid connection relay." }, - "value": "[resourceId('Microsoft.Sql/servers/encryptionProtector', parameters('sqlServerName'), 'current')]" + "value": "[resourceId('Microsoft.Web/sites/hybridConnectionNamespaces/relays', split(format('{0}/{1}/{2}', parameters('appName'), split(parameters('hybridConnectionResourceId'), '/')[8], split(parameters('hybridConnectionResourceId'), '/')[10]), '/')[0], split(format('{0}/{1}/{2}', parameters('appName'), split(parameters('hybridConnectionResourceId'), '/')[8], split(parameters('hybridConnectionResourceId'), '/')[10]), '/')[1], split(format('{0}/{1}/{2}', parameters('appName'), split(parameters('hybridConnectionResourceId'), '/')[8], split(parameters('hybridConnectionResourceId'), '/')[10]), '/')[2])]" }, "resourceGroupName": { "type": "string", "metadata": { - "description": "The resource group of the deployed encryption protector." + "description": "The name of the resource group the resource was deployed into." }, "value": "[resourceGroup().name]" } @@ -86368,53 +98189,62 @@ } }, "dependsOn": [ - "cmk_key", - "server" + "app" ] }, - "server_audit_settings": { - "condition": "[not(empty(parameters('auditSettings')))]", + "app_privateEndpoints": { + "copy": { + "name": "app_privateEndpoints", + "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]" + }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-Sql-AuditSettings', uniqueString(deployment().name, parameters('location')))]", + "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]]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "serverName": { - "value": "[parameters('name')]" - }, "name": { - "value": "[coalesce(tryGet(parameters('auditSettings'), 'name'), 'default')]" + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'name'), format('pep-{0}-{1}-{2}', last(split(resourceId('Microsoft.Web/sites', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'sites'), copyIndex()))]" }, - "state": { - "value": "[tryGet(parameters('auditSettings'), 'state')]" + "privateLinkServiceConnections": "[if(not(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true())), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.Web/sites', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'sites'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.Web/sites', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'sites')))))), createObject('value', null()))]", + "manualPrivateLinkServiceConnections": "[if(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true()), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.Web/sites', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'sites'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.Web/sites', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'sites')), 'requestMessage', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualConnectionRequestMessage'), 'Manual approval required.'))))), createObject('value', null()))]", + "subnetResourceId": { + "value": "[coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId]" }, - "auditActionsAndGroups": { - "value": "[tryGet(parameters('auditSettings'), 'auditActionsAndGroups')]" + "enableTelemetry": { + "value": "[variables('enableReferencedModulesTelemetry')]" }, - "isAzureMonitorTargetEnabled": { - "value": "[tryGet(parameters('auditSettings'), 'isAzureMonitorTargetEnabled')]" + "location": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'location'), reference(split(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location)]" }, - "isDevopsAuditEnabled": { - "value": "[tryGet(parameters('auditSettings'), 'isDevopsAuditEnabled')]" + "lock": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" }, - "isManagedIdentityInUse": { - "value": "[tryGet(parameters('auditSettings'), 'isManagedIdentityInUse')]" + "privateDnsZoneGroup": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroup')]" }, - "isStorageSecondaryKeyInUse": { - "value": "[tryGet(parameters('auditSettings'), 'isStorageSecondaryKeyInUse')]" + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'roleAssignments')]" }, - "queueDelayMs": { - "value": "[tryGet(parameters('auditSettings'), 'queueDelayMs')]" + "tags": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" }, - "retentionDays": { - "value": "[tryGet(parameters('auditSettings'), 'retentionDays')]" + "customDnsConfigs": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customDnsConfigs')]" }, - "storageAccountResourceId": { - "value": "[tryGet(parameters('auditSettings'), 'storageAccountResourceId')]" + "ipConfigurations": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'ipConfigurations')]" + }, + "applicationSecurityGroupResourceIds": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'applicationSecurityGroupResourceIds')]" + }, + "customNetworkInterfaceName": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customNetworkInterfaceName')]" } }, "template": { @@ -86424,581 +98254,699 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.34.44.8038", - "templateHash": "15469959027924260105" + "version": "0.33.13.18514", + "templateHash": "15954548978129725136" }, - "name": "Azure SQL Server Audit Settings", - "description": "This module deploys an Azure SQL Server Audit Settings." + "name": "Private Endpoints", + "description": "This module deploys a Private Endpoint." + }, + "definitions": { + "privateDnsZoneGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, + "metadata": { + "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "ipConfigurationType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource that is unique within a resource group." + } + }, + "properties": { + "type": "object", + "properties": { + "groupId": { + "type": "string", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } + } + }, + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "privateLinkServiceConnectionType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the private link service connection." + } + }, + "properties": { + "type": "object", + "properties": { + "groupIds": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." + } + }, + "privateLinkServiceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of private link service." + } + }, + "requestMessage": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." + } + } + }, + "metadata": { + "description": "Required. Properties of private link service connection." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "customDnsConfigType": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. FQDN that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "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." + } + } + }, + "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.5.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": { + "__bicep_imported_from!": { + "sourceTemplate": "private-dns-zone-group/main.bicep" + } + } + }, + "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.5.1" + } + } + } }, "parameters": { "name": { "type": "string", "metadata": { - "description": "Required. The name of the audit settings." + "description": "Required. Name of the private endpoint resource to create." } }, - "serverName": { + "subnetResourceId": { "type": "string", "metadata": { - "description": "Conditional. The Name of SQL Server. Required if the template is used in a standalone deployment." + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." } }, - "state": { + "applicationSecurityGroupResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Application security groups in which the private endpoint IP configuration is included." + } + }, + "customNetworkInterfaceName": { "type": "string", - "defaultValue": "Enabled", - "allowedValues": [ - "Enabled", - "Disabled" - ], + "nullable": true, "metadata": { - "description": "Optional. Specifies the state of the audit. If state is Enabled, storageEndpoint or isAzureMonitorTargetEnabled are required." + "description": "Optional. The custom name of the network interface attached to the private endpoint." } }, - "auditActionsAndGroups": { + "ipConfigurations": { "type": "array", "items": { - "type": "string" + "$ref": "#/definitions/ipConfigurationType" }, - "defaultValue": [ - "BATCH_COMPLETED_GROUP", - "SUCCESSFUL_DATABASE_AUTHENTICATION_GROUP", - "FAILED_DATABASE_AUTHENTICATION_GROUP" - ], + "nullable": true, "metadata": { - "description": "Optional. Specifies the Actions-Groups and Actions to audit." + "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." } }, - "isAzureMonitorTargetEnabled": { - "type": "bool", - "defaultValue": true, + "privateDnsZoneGroup": { + "$ref": "#/definitions/privateDnsZoneGroupType", + "nullable": true, "metadata": { - "description": "Optional. Specifies whether audit events are sent to Azure Monitor." + "description": "Optional. The private DNS zone group to configure for the private endpoint." } }, - "isDevopsAuditEnabled": { - "type": "bool", - "defaultValue": false, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", "metadata": { - "description": "Optional. Specifies the state of devops audit. If state is Enabled, devops logs will be sent to Azure Monitor." + "description": "Optional. Location for all Resources." } }, - "isManagedIdentityInUse": { - "type": "bool", - "defaultValue": false, + "lock": { + "$ref": "#/definitions/lockType", + "nullable": true, "metadata": { - "description": "Optional. Specifies whether Managed Identity is used to access blob storage." + "description": "Optional. The lock settings of the service." } }, - "isStorageSecondaryKeyInUse": { - "type": "bool", - "defaultValue": false, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, "metadata": { - "description": "Optional. Specifies whether storageAccountAccessKey value is the storage's secondary key." + "description": "Optional. Array of role assignments to create." } }, - "queueDelayMs": { - "type": "int", - "defaultValue": 1000, + "tags": { + "type": "object", + "nullable": true, "metadata": { - "description": "Optional. Specifies the amount of time in milliseconds that can elapse before audit actions are forced to be processed." + "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." } }, - "retentionDays": { - "type": "int", - "defaultValue": 90, + "customDnsConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/customDnsConfigType" + }, + "nullable": true, "metadata": { - "description": "Optional. Specifies the number of days to keep in the audit logs in the storage account." + "description": "Optional. Custom DNS configurations." } }, - "storageAccountResourceId": { - "type": "string", - "defaultValue": "", + "manualPrivateLinkServiceConnections": { + "type": "array", + "items": { + "$ref": "#/definitions/privateLinkServiceConnectionType" + }, + "nullable": true, "metadata": { - "description": "Optional. A blob storage to hold the auditing storage account." + "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." + } + }, + "privateLinkServiceConnections": { + "type": "array", + "items": { + "$ref": "#/definitions/privateLinkServiceConnectionType" + }, + "nullable": true, + "metadata": { + "description": "Conditional. A grouping of information about the connection to the remote resource. Required if `manualPrivateLinkServiceConnections` is empty." + } + }, + "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": { - "server": { - "existing": true, - "type": "Microsoft.Sql/servers", - "apiVersion": "2023-08-01-preview", - "name": "[parameters('serverName')]" + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.10.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" + } + } + } + } }, - "auditSettings": { - "type": "Microsoft.Sql/servers/auditingSettings", - "apiVersion": "2023-08-01-preview", - "name": "[format('{0}/{1}', parameters('serverName'), parameters('name'))]", + "privateEndpoint": { + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-11-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", "properties": { - "state": "[parameters('state')]", - "auditActionsAndGroups": "[parameters('auditActionsAndGroups')]", - "isAzureMonitorTargetEnabled": "[parameters('isAzureMonitorTargetEnabled')]", - "isDevopsAuditEnabled": "[parameters('isDevopsAuditEnabled')]", - "isManagedIdentityInUse": "[parameters('isManagedIdentityInUse')]", - "isStorageSecondaryKeyInUse": "[parameters('isStorageSecondaryKeyInUse')]", - "queueDelayMs": "[parameters('queueDelayMs')]", - "retentionDays": "[parameters('retentionDays')]", - "storageAccountAccessKey": "[if(and(not(empty(parameters('storageAccountResourceId'))), not(parameters('isManagedIdentityInUse'))), listKeys(parameters('storageAccountResourceId'), '2019-06-01').keys[0].value, null())]", - "storageAccountSubscriptionId": "[if(not(empty(parameters('storageAccountResourceId'))), split(parameters('storageAccountResourceId'), '/')[2], null())]", - "storageEndpoint": "[if(not(empty(parameters('storageAccountResourceId'))), format('https://{0}.blob.{1}', last(split(parameters('storageAccountResourceId'), '/')), environment().suffixes.storage), null())]" + "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')]" + } } }, - "storageAccount_sbdc_rbac": { - "condition": "[and(parameters('isManagedIdentityInUse'), not(empty(parameters('storageAccountResourceId'))))]", + "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": "[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": "2022-09-01", - "name": "[format('{0}-stau-rbac', parameters('serverName'))]", + "name": "[format('{0}-PrivateEndpoint-PrivateDnsZoneGroup', uniqueString(deployment().name))]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "storageAccountName": { - "value": "[last(split(parameters('storageAccountResourceId'), '/'))]" + "name": { + "value": "[tryGet(parameters('privateDnsZoneGroup'), 'name')]" }, - "managedIdentityPrincipalId": "[if(equals(reference('server', '2023-08-01-preview', 'full').identity.type, 'UserAssigned'), createObject('value', filter(items(reference('server', '2023-08-01-preview', 'full').identity.userAssignedIdentities), lambda('identity', equals(lambdaVariables('identity').key, reference('server').primaryUserAssignedIdentityId)))[0].value.principalId), createObject('value', reference('server', '2023-08-01-preview', 'full').identity.principalId))]" + "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.34.44.8038", - "templateHash": "11839727322069180104" + "version": "0.33.13.18514", + "templateHash": "5440815542537978381" + }, + "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 + } } }, "parameters": { - "storageAccountName": { - "type": "string" + "privateEndpointName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment." + } }, - "managedIdentityPrincipalId": { - "type": "string" + "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": [ - { - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Storage/storageAccounts/{0}', parameters('storageAccountName'))]", - "name": "[guid(format('{0}-{1}-Storage-Blob-Data-Contributor', resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName')), parameters('managedIdentityPrincipalId')))]", + "variables": { + "copy": [ + { + "name": "privateDnsZoneConfigsVar", + "count": "[length(parameters('privateDnsZoneConfigs'))]", + "input": { + "name": "[coalesce(tryGet(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')], 'name'), last(split(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId, '/')))]", + "properties": { + "privateDnsZoneId": "[parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId]" + } + } + } + ] + }, + "resources": { + "privateEndpoint": { + "existing": true, + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-11-01", + "name": "[parameters('privateEndpointName')]" + }, + "privateDnsZoneGroup": { + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2023-11-01", + "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", "properties": { - "roleDefinitionId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe')]", - "principalId": "[parameters('managedIdentityPrincipalId')]", - "principalType": "ServicePrincipal" + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" } } - ] - } - }, - "dependsOn": [ - "server" - ] - } - }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the deployed audit settings." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the deployed audit settings." - }, - "value": "[resourceId('Microsoft.Sql/servers/auditingSettings', parameters('serverName'), parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group of the deployed audit settings." - }, - "value": "[resourceGroup().name]" - } - } - } - }, - "dependsOn": [ - "server" - ] - }, - "secretsExport": { - "condition": "[not(equals(parameters('secretsExportConfiguration'), null()))]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-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]]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "keyVaultName": { - "value": "[last(split(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '/'))]" - }, - "secretsToSet": { - "value": "[union(createArray(), if(contains(parameters('secretsExportConfiguration'), 'sqlAdminPasswordSecretName'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'sqlAdminPasswordSecretName'), 'value', parameters('administratorLoginPassword'))), createArray()), if(contains(parameters('secretsExportConfiguration'), 'sqlAzureConnectionStringSercretName'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'sqlAzureConnectionStringSercretName'), 'value', format('Server={0}; Database={1}; User={2}; Password={3}', reference('server').fullyQualifiedDomainName, if(not(empty(parameters('databases'))), parameters('databases')[0].name, ''), parameters('administratorLogin'), parameters('administratorLoginPassword')))), createArray()))]" - } - }, - "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.34.44.8038", - "templateHash": "12919538841236982879" - } - }, - "definitions": { - "secretSetOutputType": { - "type": "object", - "properties": { - "secretResourceId": { - "type": "string", - "metadata": { - "description": "The resourceId of the exported secret." - } - }, - "secretUri": { - "type": "string", - "metadata": { - "description": "The secret URI of the exported secret." - } - }, - "secretUriWithVersion": { - "type": "string", - "metadata": { - "description": "The secret URI with version of the exported secret." - } - } - }, - "metadata": { - "description": "An AVM-aligned type for the output of the secret set via the secrets export feature.", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } - } - }, - "secretToSetType": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the secret to set." - } - }, - "value": { - "type": "securestring", - "metadata": { - "description": "Required. The value of the secret to set." + }, + "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]" + } } } }, - "metadata": { - "description": "An AVM-aligned type for the secret to set via the secrets export feature.", - "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" - } - } + "dependsOn": [ + "privateEndpoint" + ] } }, - "parameters": { - "keyVaultName": { + "outputs": { + "resourceGroupName": { "type": "string", "metadata": { - "description": "Required. The name of the Key Vault to set the secrets in." - } - }, - "secretsToSet": { - "type": "array", - "items": { - "$ref": "#/definitions/secretToSetType" + "description": "The resource group the private endpoint was deployed into." }, - "metadata": { - "description": "Required. The secrets to set in the Key Vault." - } - } - }, - "resources": { - "keyVault": { - "existing": true, - "type": "Microsoft.KeyVault/vaults", - "apiVersion": "2022-07-01", - "name": "[parameters('keyVaultName')]" + "value": "[resourceGroup().name]" }, - "secrets": { - "copy": { - "name": "secrets", - "count": "[length(parameters('secretsToSet'))]" - }, - "type": "Microsoft.KeyVault/vaults/secrets", - "apiVersion": "2023-07-01", - "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('secretsToSet')[copyIndex()].name)]", - "properties": { - "value": "[parameters('secretsToSet')[copyIndex()].value]" - } - } - }, - "outputs": { - "secretsSet": { - "type": "array", - "items": { - "$ref": "#/definitions/secretSetOutputType" - }, + "resourceId": { + "type": "string", "metadata": { - "description": "The references to the secrets exported to the provided Key Vault." - }, - "copy": { - "count": "[length(range(0, length(coalesce(parameters('secretsToSet'), createArray()))))]", - "input": { - "secretResourceId": "[resourceId('Microsoft.KeyVault/vaults/secrets', parameters('keyVaultName'), parameters('secretsToSet')[range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()]].name)]", - "secretUri": "[reference(format('secrets[{0}]', range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()])).secretUri]", - "secretUriWithVersion": "[reference(format('secrets[{0}]', range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()])).secretUriWithVersion]" - } - } - } - } - } - }, - "dependsOn": [ - "server" - ] - }, - "failover_groups": { - "copy": { - "name": "failover_groups", - "count": "[length(parameters('failoverGroups'))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-Sql-FailoverGroup-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[parameters('failoverGroups')[copyIndex()].name]" - }, - "tags": { - "value": "[coalesce(tryGet(parameters('failoverGroups')[copyIndex()], 'tags'), parameters('tags'))]" - }, - "serverName": { - "value": "[parameters('name')]" - }, - "databases": { - "value": "[parameters('failoverGroups')[copyIndex()].databases]" - }, - "partnerServers": { - "value": "[parameters('failoverGroups')[copyIndex()].partnerServers]" - }, - "readOnlyEndpoint": { - "value": "[tryGet(parameters('failoverGroups')[copyIndex()], 'readOnlyEndpoint')]" - }, - "readWriteEndpoint": { - "value": "[parameters('failoverGroups')[copyIndex()].readWriteEndpoint]" - }, - "secondaryType": { - "value": "[parameters('failoverGroups')[copyIndex()].secondaryType]" - } - }, - "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.34.44.8038", - "templateHash": "5841212065211909434" - }, - "name": "Azure SQL Server failover group", - "description": "This module deploys Azure SQL Server failover group." - }, - "definitions": { - "failoverGroupReadOnlyEndpointType": { - "type": "object", - "properties": { - "failoverPolicy": { - "type": "string", - "allowedValues": [ - "Disabled", - "Enabled" - ], - "metadata": { - "description": "Required. Failover policy of the read-only endpoint for the failover group." - } - }, - "targetServer": { - "type": "string", - "metadata": { - "description": "Required. The target partner server where the read-only endpoint points to." - } - } + "description": "The resource ID of the private endpoint." }, - "metadata": { - "__bicep_export!": true - } + "value": "[resourceId('Microsoft.Network/privateEndpoints', parameters('name'))]" }, - "failoverGroupReadWriteEndpointType": { - "type": "object", - "properties": { - "failoverPolicy": { - "type": "string", - "allowedValues": [ - "Automatic", - "Manual" - ], - "metadata": { - "description": "Required. Failover policy of the read-write endpoint for the failover group. If failoverPolicy is Automatic then failoverWithDataLossGracePeriodMinutes is required." - } - }, - "failoverWithDataLossGracePeriodMinutes": { - "type": "int", - "nullable": true, - "metadata": { - "description": "Optional. Grace period before failover with data loss is attempted for the read-write endpoint." - } - } - }, - "metadata": { - "__bicep_export!": true - } - } - }, - "parameters": { "name": { "type": "string", "metadata": { - "description": "Required. The name of the failover group." - } + "description": "The name of the private endpoint." + }, + "value": "[parameters('name')]" }, - "serverName": { + "location": { "type": "string", "metadata": { - "description": "Conditional. The Name of SQL Server. Required if the template is used in a standalone deployment." - } + "description": "The location the resource was deployed into." + }, + "value": "[reference('privateEndpoint', '2023-11-01', 'full').location]" }, - "databases": { + "customDnsConfigs": { "type": "array", "items": { - "type": "string" + "$ref": "#/definitions/customDnsConfigType" }, "metadata": { - "description": "Required. List of databases in the failover group." - } + "description": "The custom DNS configurations of the private endpoint." + }, + "value": "[reference('privateEndpoint').customDnsConfigs]" }, - "partnerServers": { + "networkInterfaceResourceIds": { "type": "array", "items": { "type": "string" }, "metadata": { - "description": "Required. List of the partner servers for the failover group." - } - }, - "readOnlyEndpoint": { - "$ref": "#/definitions/failoverGroupReadOnlyEndpointType", - "nullable": true, - "metadata": { - "description": "Optional. Read-only endpoint of the failover group instance." - } - }, - "readWriteEndpoint": { - "$ref": "#/definitions/failoverGroupReadWriteEndpointType", - "metadata": { - "description": "Required. Read-write endpoint of the failover group instance." - } - }, - "secondaryType": { - "type": "string", - "allowedValues": [ - "Geo", - "Standby" - ], - "metadata": { - "description": "Required. Databases secondary type on partner server." - } - }, - "tags": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Tags of the resource." - } - } - }, - "resources": { - "server": { - "existing": true, - "type": "Microsoft.Sql/servers", - "apiVersion": "2023-08-01-preview", - "name": "[parameters('serverName')]" - }, - "failoverGroup": { - "type": "Microsoft.Sql/servers/failoverGroups", - "apiVersion": "2024-05-01-preview", - "name": "[format('{0}/{1}', parameters('serverName'), parameters('name'))]", - "tags": "[parameters('tags')]", - "properties": { - "copy": [ - { - "name": "databases", - "count": "[length(parameters('databases'))]", - "input": "[resourceId('Microsoft.Sql/servers/databases', parameters('serverName'), parameters('databases')[copyIndex('databases')])]" - }, - { - "name": "partnerServers", - "count": "[length(parameters('partnerServers'))]", - "input": { - "id": "[resourceId(resourceGroup().name, 'Microsoft.Sql/servers', parameters('partnerServers')[copyIndex('partnerServers')])]" - } - } - ], - "readOnlyEndpoint": "[if(not(empty(parameters('readOnlyEndpoint'))), createObject('failoverPolicy', parameters('readOnlyEndpoint').failoverPolicy, 'targetServer', resourceId(resourceGroup().name, 'Microsoft.Sql/servers', parameters('readOnlyEndpoint').targetServer)), null())]", - "readWriteEndpoint": "[parameters('readWriteEndpoint')]", - "secondaryType": "[parameters('secondaryType')]" - } - } - }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the deployed failover group." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the deployed failover group." + "description": "The resource IDs of the network interfaces associated with the private endpoint." }, - "value": "[resourceId('Microsoft.Sql/servers/failoverGroups', parameters('serverName'), parameters('name'))]" + "value": "[map(reference('privateEndpoint').networkInterfaces, lambda('nic', lambdaVariables('nic').id))]" }, - "resourceGroupName": { + "groupId": { "type": "string", + "nullable": true, "metadata": { - "description": "The resource group of the deployed failover group." + "description": "The group Id for the private endpoint Group." }, - "value": "[resourceGroup().name]" + "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": [ - "server", - "server_databases" + "app" ] } }, @@ -87006,28 +98954,41 @@ "name": { "type": "string", "metadata": { - "description": "The name of the deployed SQL server." + "description": "The name of the site." }, "value": "[parameters('name')]" }, "resourceId": { "type": "string", "metadata": { - "description": "The resource ID of the deployed SQL server." + "description": "The resource ID of the site." }, - "value": "[resourceId('Microsoft.Sql/servers', parameters('name'))]" + "value": "[resourceId('Microsoft.Web/sites', parameters('name'))]" }, - "fullyQualifiedDomainName": { - "type": "string", + "slots": { + "type": "array", "metadata": { - "description": "The fully qualified domain name of the deployed SQL server." + "description": "The list of the slots." }, - "value": "[reference('server').fullyQualifiedDomainName]" + "copy": { + "count": "[length(coalesce(parameters('slots'), createArray()))]", + "input": "[format('{0}-Slot-{1}', uniqueString(deployment().name, parameters('location')), coalesce(parameters('slots'), createArray())[copyIndex()].name)]" + } + }, + "slotResourceIds": { + "type": "array", + "metadata": { + "description": "The list of the slot resource ids." + }, + "copy": { + "count": "[length(coalesce(parameters('slots'), createArray()))]", + "input": "[reference(format('app_slots[{0}]', copyIndex())).outputs.resourceId.value]" + } }, "resourceGroupName": { "type": "string", "metadata": { - "description": "The resource group of the deployed SQL server." + "description": "The resource group the site was deployed into." }, "value": "[resourceGroup().name]" }, @@ -87037,21 +98998,41 @@ "metadata": { "description": "The principal ID of the system assigned identity." }, - "value": "[tryGet(tryGet(reference('server', '2023-08-01-preview', 'full'), 'identity'), 'principalId')]" + "value": "[tryGet(tryGet(reference('app', '2024-04-01', 'full'), 'identity'), 'principalId')]" + }, + "slotSystemAssignedMIPrincipalIds": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "The principal ID of the system assigned identity of slots." + }, + "copy": { + "count": "[length(coalesce(parameters('slots'), createArray()))]", + "input": "[coalesce(tryGet(tryGet(reference(format('app_slots[{0}]', copyIndex())).outputs, 'systemAssignedMIPrincipalId'), 'value'), '')]" + } }, "location": { "type": "string", "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('server', '2023-08-01-preview', 'full').location]" + "value": "[reference('app', '2024-04-01', 'full').location]" }, - "exportedSecrets": { - "$ref": "#/definitions/secretsOutputType", + "defaultHostname": { + "type": "string", "metadata": { - "description": "A hashtable of references to the secrets exported to the provided Key Vault. The key of each reference is each secret's name." + "description": "Default hostname of the app." }, - "value": "[if(not(equals(parameters('secretsExportConfiguration'), null())), toObject(reference('secretsExport').outputs.secretsSet.value, lambda('secret', last(split(lambdaVariables('secret').secretResourceId, '/'))), lambda('secret', lambdaVariables('secret'))), createObject())]" + "value": "[reference('app').defaultHostName]" + }, + "customDomainVerificationId": { + "type": "string", + "metadata": { + "description": "Unique identifier that verifies the custom domains assigned to the app. Customer will add this ID to a txt record for verification." + }, + "value": "[reference('app').customDomainVerificationId]" }, "privateEndpoints": { "type": "array", @@ -87059,45 +99040,317 @@ "$ref": "#/definitions/privateEndpointOutputType" }, "metadata": { - "description": "The private endpoints of the SQL server." + "description": "The private endpoints of the site." }, "copy": { "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]", "input": { - "name": "[reference(format('server_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", - "resourceId": "[reference(format('server_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", - "groupId": "[tryGet(tryGet(reference(format('server_privateEndpoints[{0}]', copyIndex())).outputs, 'groupId'), 'value')]", - "customDnsConfigs": "[reference(format('server_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfigs.value]", - "networkInterfaceResourceIds": "[reference(format('server_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceResourceIds.value]" + "name": "[reference(format('app_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", + "resourceId": "[reference(format('app_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", + "groupId": "[tryGet(tryGet(reference(format('app_privateEndpoints[{0}]', copyIndex())).outputs, 'groupId'), 'value')]", + "customDnsConfigs": "[reference(format('app_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfigs.value]", + "networkInterfaceResourceIds": "[reference(format('app_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceResourceIds.value]" } } + }, + "slotPrivateEndpoints": { + "type": "array", + "metadata": { + "description": "The private endpoints of the slots." + }, + "copy": { + "count": "[length(coalesce(parameters('slots'), createArray()))]", + "input": "[reference(format('app_slots[{0}]', copyIndex())).outputs.privateEndpoints.value]" + } + }, + "outboundIpAddresses": { + "type": "string", + "metadata": { + "description": "The outbound IP addresses of the app." + }, + "value": "[reference('app').outboundIpAddresses]" } } } }, "dependsOn": [ - "privateDnsZone" + "appInsights", + "appServicePlan", + "userAssignedIdentity" ] } }, "outputs": { "resourceId": { "type": "string", - "value": "[reference('sqlServer').outputs.resourceId.value]" + "value": "[reference('appService').outputs.resourceId.value]" }, "name": { "type": "string", - "value": "[reference('sqlServer').outputs.name.value]" + "value": "[reference('appService').outputs.name.value]" + }, + "uri": { + "type": "string", + "value": "[format('https://{0}', reference('appService').outputs.defaultHostname.value)]" + }, + "appServicePlanResourceId": { + "type": "string", + "value": "[reference('appServicePlan').outputs.resourceId.value]" + }, + "appServicePlanName": { + "type": "string", + "value": "[reference('appServicePlan').outputs.name.value]" } } } }, "dependsOn": [ + "aiSearch", + "appIdentity", + "applicationInsights", + "cognitiveServices", + "cosmosDb", + "keyvault", + "logAnalyticsWorkspace", "network" ] + }, + "appSample": { + "condition": "[variables('deploySampleApp')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "app-sample-deployment", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "aiSearchName": "[if(parameters('searchEnabled'), createObject('value', reference('aiSearch').outputs.name.value), createObject('value', ''))]", + "cognitiveServicesName": { + "value": "[reference('cognitiveServices').outputs.aiServicesName.value]" + }, + "aiEmbeddingModelDeployment": { + "value": "[parameters('aiEmbeddingModelDeployment')]" + }, + "networkIsolation": { + "value": "[parameters('networkIsolation')]" + }, + "virtualMachinePrincipalId": "[if(parameters('networkIsolation'), createObject('value', reference('virtualMachine').outputs.principalId.value), createObject('value', ''))]", + "vmName": "[if(parameters('networkIsolation'), createObject('value', reference('virtualMachine').outputs.name.value), createObject('value', ''))]" + }, + "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.1.42791", + "templateHash": "5085333471373527815" + } + }, + "definitions": { + "modelDeploymentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Cognitive Services account deployment model. The modelName will be used by default if not specified." + } + }, + "modelName": { + "type": "string", + "metadata": { + "description": "Required. The format of the Cognitive Services account deployment model." + } + }, + "version": { + "type": "string", + "metadata": { + "description": "Required. The version of the Cognitive Services account deployment model." + } + }, + "capacity": { + "type": "int", + "metadata": { + "description": "Required. The capacity of the resource model definition representing SKU." + } + } + }, + "metadata": { + "description": "The AI model deployment type for Cognitive Services account.", + "__bicep_imported_from!": { + "sourceTemplate": "customTypes.bicep" + } + } + } + }, + "parameters": { + "aiSearchName": { + "type": "string", + "metadata": { + "description": "The name of the existing Azure AI Search service to be referenced." + } + }, + "cognitiveServicesName": { + "type": "string", + "metadata": { + "description": "The name of the existing Azure Cognitive Services account to be referenced." + } + }, + "aiEmbeddingModelDeployment": { + "$ref": "#/definitions/modelDeploymentType", + "metadata": { + "description": "Specifies the AI embedding model to use for the AI Foundry deployment. This is the model used for text embeddings in AI Foundry. NOTE: Any adjustments to this parameter's values must also be made on the aiDeploymentsLocation metadata in the main.bicep file." + } + }, + "networkIsolation": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Specifies whether network isolation is enabled. When true, Foundry and related components will be deployed, network access parameters will be set to Disabled." + } + }, + "virtualMachinePrincipalId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Principal ID (objectId) of the VM’s managed identity" + } + }, + "vmName": { + "type": "string", + "metadata": { + "description": "The name of the virtual machine where the script will be executed." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "The location for the resources." + } + }, + "installtionScript": { + "type": "string", + "defaultValue": "https://raw.githubusercontent.com/microsoft/Deploy-Your-AI-Application-In-Production/main/scripts/install_python.ps1", + "metadata": { + "description": "The URL of the script to be executed on the virtual machine." + } + } + }, + "variables": { + "searchIndexContributorRoleId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8ebe5a00-799e-43f5-93ac-243d3dce84a7')]", + "searchServiceContributorRoleId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7ca78c08-252a-4471-8644-bb5ff32d4ba0')]", + "openAiUserRole": "Cognitive Services OpenAI User" + }, + "resources": { + "vm": { + "condition": "[parameters('networkIsolation')]", + "existing": true, + "type": "Microsoft.Compute/virtualMachines", + "apiVersion": "2023-03-01", + "name": "[parameters('vmName')]" + }, + "customScriptExt": { + "condition": "[parameters('networkIsolation')]", + "type": "Microsoft.Compute/virtualMachines/extensions", + "apiVersion": "2023-03-01", + "name": "[format('{0}/{1}', parameters('vmName'), 'CustomScriptExtension')]", + "location": "[parameters('location')]", + "properties": { + "publisher": "Microsoft.Compute", + "type": "CustomScriptExtension", + "typeHandlerVersion": "1.10", + "autoUpgradeMinorVersion": true, + "settings": { + "fileUris": [ + "[parameters('installtionScript')]" + ], + "commandToExecute": "powershell -ExecutionPolicy Bypass -File install_python.ps1" + } + }, + "dependsOn": [ + "roleAssignment", + "searchIndexRoleAssignment", + "searchServiceRoleAssignment" + ] + }, + "cognitiveServicesRes": { + "existing": true, + "type": "Microsoft.CognitiveServices/accounts", + "apiVersion": "2025-04-01-preview", + "name": "[parameters('cognitiveServicesName')]" + }, + "aiSearchResource": { + "existing": true, + "type": "Microsoft.Search/searchServices", + "apiVersion": "2023-11-01", + "name": "[parameters('aiSearchName')]" + }, + "searchIndexRoleAssignment": { + "condition": "[parameters('networkIsolation')]", + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Search/searchServices/{0}', parameters('aiSearchName'))]", + "name": "[guid(resourceId('Microsoft.Search/searchServices', parameters('aiSearchName')), parameters('virtualMachinePrincipalId'), 'SearchIndexDataContributor')]", + "properties": { + "roleDefinitionId": "[variables('searchIndexContributorRoleId')]", + "principalId": "[parameters('virtualMachinePrincipalId')]", + "principalType": "ServicePrincipal" + } + }, + "searchServiceRoleAssignment": { + "condition": "[parameters('networkIsolation')]", + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Search/searchServices/{0}', parameters('aiSearchName'))]", + "name": "[guid(resourceId('Microsoft.Search/searchServices', parameters('aiSearchName')), parameters('virtualMachinePrincipalId'), 'SearchServiceContributor')]", + "properties": { + "roleDefinitionId": "[variables('searchServiceContributorRoleId')]", + "principalId": "[parameters('virtualMachinePrincipalId')]", + "principalType": "ServicePrincipal" + } + }, + "roleAssignment": { + "condition": "[parameters('networkIsolation')]", + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.CognitiveServices/accounts/{0}', parameters('cognitiveServicesName'))]", + "name": "[guid(resourceId('Microsoft.CognitiveServices/accounts', parameters('cognitiveServicesName')), parameters('virtualMachinePrincipalId'), variables('openAiUserRole'))]", + "properties": { + "principalId": "[parameters('virtualMachinePrincipalId')]", + "roleDefinitionId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e0bd9bd-7b93-4f28-af87-19fc36ad61bd')]", + "principalType": "ServicePrincipal" + } + } + } + } + }, + "dependsOn": [ + "aiSearch", + "cognitiveServices", + "keyvault", + "virtualMachine" + ] } }, "outputs": { + "AZURE_SEARCH_ENDPOINT": { + "type": "string", + "value": "[if(parameters('searchEnabled'), format('https://{0}.search.windows.net', reference('aiSearch').outputs.name.value), '')]" + }, + "AZURE_OPENAI_ENDPOINT": { + "type": "string", + "value": "[reference('cognitiveServices').outputs.aiServicesEndpoint.value]" + }, + "EMBEDDING_MODEL_NAME": { + "type": "string", + "value": "[parameters('aiEmbeddingModelDeployment').modelName]" + }, "AZURE_KEY_VAULT_NAME": { "type": "string", "value": "[reference('keyvault').outputs.name.value]" @@ -87152,11 +99405,11 @@ }, "AZURE_VIRTUAL_NETWORK_NAME": { "type": "string", - "value": "[if(parameters('networkIsolation'), reference('network').outputs.virtualNetworkName.value, '')]" + "value": "[if(parameters('networkIsolation'), reference('network').outputs.name.value, '')]" }, "AZURE_VIRTUAL_NETWORK_SUBNET_NAME": { "type": "string", - "value": "[if(parameters('networkIsolation'), reference('network').outputs.vmSubnetName.value, '')]" + "value": "[if(parameters('networkIsolation'), reference('network').outputs.defaultSubnetName.value, '')]" }, "AZURE_SQL_SERVER_NAME": { "type": "string", @@ -87169,6 +99422,14 @@ "AZURE_COSMOS_ACCOUNT_NAME": { "type": "string", "value": "[if(parameters('cosmosDbEnabled'), reference('cosmosDb').outputs.cosmosDBname.value, '')]" + }, + "SAMPLE_APP_URL": { + "type": "string", + "value": "[if(variables('deploySampleApp'), reference('appService').outputs.uri.value, '')]" + }, + "AZURE_APP_SAMPLE_ENABLED": { + "type": "bool", + "value": "[variables('deploySampleApp')]" } } } \ No newline at end of file diff --git a/infra/main.parameters.json b/infra/main.parameters.json index 7f5556c..9229397 100644 --- a/infra/main.parameters.json +++ b/infra/main.parameters.json @@ -47,8 +47,14 @@ "documentIntelligenceEnabled": { "value": "${AZURE_AI_DOC_INTELLIGENCE_ENABLED}" }, - "bingGroundingEnabled": { - "value": "${AZURE_BING_GROUNDING_ENABLED}" + "appSampleEnabled": { + "value": "${AZURE_APP_SAMPLE_ENABLED}" + }, + "authClientId": { + "value": "${AZURE_AUTH_CLIENT_ID}" + }, + "authClientSecret": { + "value": "${AZURE_AUTH_CLIENT_SECRET}" }, "aiEmbeddingModelDeployment": { "value": { @@ -63,6 +69,19 @@ "version": "2024-05-13", "capacity": 150 } + }, + "cosmosDatabases": { + "value": [ + { + "name": "db_conversation_history", + "containers": [ + { + "name": "conversations", + "paths": ["/userId"] + } + ] + } + ] } } } \ No newline at end of file diff --git a/infra/modules/ai-foundry-project/main.bicep b/infra/modules/ai-foundry-project/main.bicep index 5c30883..83cefd4 100644 --- a/infra/modules/ai-foundry-project/main.bicep +++ b/infra/modules/ai-foundry-project/main.bicep @@ -46,7 +46,6 @@ resource cosmosDBAccount 'Microsoft.DocumentDB/databaseAccounts@2025-05-01-previ name: cosmosDBname } - resource project 'Microsoft.CognitiveServices/accounts/projects@2025-04-01-preview' = { name: defaultProjectName parent: foundryAccount @@ -111,6 +110,5 @@ resource project_connection_cosmosdb 'Microsoft.CognitiveServices/accounts/proje } } - output projectId string = project.id output projectName string = project.name diff --git a/infra/modules/aisearch.bicep b/infra/modules/aisearch.bicep index 1e51de2..0221f53 100644 --- a/infra/modules/aisearch.bicep +++ b/infra/modules/aisearch.bicep @@ -38,7 +38,7 @@ module privateDnsZone 'br/public:avm/res/network/private-dns-zone:0.7.0' = if (n var nameFormatted = take(toLower(name), 60) -module aiSearch 'br/public:avm/res/search/search-service:0.9.2' = { +module aiSearch 'br/public:avm/res/search/search-service:0.10.0' = { name: take('${nameFormatted}-search-services-deployment', 64) #disable-next-line no-unnecessary-dependson dependsOn: [privateDnsZone] // required due to optional flags that could change dependency @@ -50,6 +50,9 @@ module aiSearch 'br/public:avm/res/search/search-service:0.9.2' = { systemAssigned: true } publicNetworkAccess: networkIsolation ? 'Disabled' : 'Enabled' + networkRuleSet: { + bypass: 'AzureServices' + } disableLocalAuth: true sku: 'standard' partitionCount: 1 @@ -81,3 +84,4 @@ module aiSearch 'br/public:avm/res/search/search-service:0.9.2' = { output resourceId string = aiSearch.outputs.resourceId output name string = aiSearch.outputs.name output systemAssignedMIPrincipalId string = aiSearch.outputs.?systemAssignedMIPrincipalId ?? '' + diff --git a/infra/modules/appservice.bicep b/infra/modules/appservice.bicep new file mode 100644 index 0000000..5716b6e --- /dev/null +++ b/infra/modules/appservice.bicep @@ -0,0 +1,316 @@ +@description('Name of the App Service resource.') +param name string + +@description('Specifies the location for all the Azure resources.') +param location string + +@description('Optional. Tags to be applied to the resources.') +param tags object = {} + +@allowed(['B1', 'B2', 'B3', 'P0V3', 'P1V3', 'P2V3', 'P3V3', 'P1mv3', 'P2mv3', 'P3mv3', 'P4mv3', 'P5mv3']) +@description('The SKU name for the App Service Plan.') +param skuName string + +@description('The SKU capacity for the App Service Plan.') +@allowed([1, 2, 3]) +param skuCapacity int + +@description('Resource ID of the virtual network subnet to integrate the App Service.') +param virtualNetworkSubnetId string + +@description('Resource Name of the user-assigned managed identity to assign to the App Service.') +param userAssignedIdentityName string + +@description('Resource ID of the Log Analytics workspace to use for diagnostic settings.') +param logAnalyticsWorkspaceResourceId string + +@description('Name of an existing Application Insights resource for the App Service.') +param appInsightsName string + +@description('Name of existing Key Vault to read secrets.') +param keyVaultName string + +@description('Full path to container image.') +param imagePath string + +@description('Tag for the image version.') +param imageTag string + +@description('Auth configuration for the App Service when registering Entra Identity Provider') +param authProvider authIdentityProvider + +@description('Cosmos DB configuration for the App Service for storing conversation history.') +param cosmosDbConfiguration cosmosDbConfig + +@description('Azure Search configuration for the App Service for searching vector content as part of the RAG chat pattern.') +param searchServiceConfiguration searchServiceConfig + +@description('OpenAI configuration for the App Service for embedding and GPT model.') +param openAIConfiguration openAIConfig + +resource appInsights 'Microsoft.Insights/components@2020-02-02' existing = { + name: appInsightsName +} + +resource userAssignedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2024-11-30' existing = { + name: userAssignedIdentityName +} + +resource keyVault 'Microsoft.KeyVault/vaults@2024-11-01' existing = { + name: keyVaultName +} + +var nameFormatted = take(toLower(name), 55) + +module appServicePlan 'br/public:avm/res/web/serverfarm:0.4.1' = { + name: take('${nameFormatted}-app-service-plan-deployment', 64) + params: { + name: 'asp-${nameFormatted}' + location: location + tags: tags + kind: 'linux' + skuName: skuName + skuCapacity: skuCapacity + reserved: true + zoneRedundant: startsWith(skuName, 'F') || startsWith(skuName, 'B') || skuCapacity == 1 ? false : true + diagnosticSettings: [ + { + metricCategories: [ + { + category: 'AllMetrics' + } + ] + name: 'customSetting' + workspaceResourceId: logAnalyticsWorkspaceResourceId + } + ] + } +} + +module appService 'br/public:avm/res/web/site:0.15.1' = { + name: take('${nameFormatted}-app-service-deployment', 64) + params: { + name: nameFormatted + location: location + tags: tags + kind: 'app,linux,container' + serverFarmResourceId: appServicePlan.outputs.resourceId + appInsightResourceId: appInsights.id + virtualNetworkSubnetId: virtualNetworkSubnetId + keyVaultAccessIdentityResourceId: userAssignedIdentity.id + managedIdentities: { + userAssignedResourceIds: [userAssignedIdentity.id] + } + logsConfiguration: { + applicationLogs: { + fileSystem: { + level: 'Information' + } + } + detailedErrorMessages: { + enabled: true + } + failedRequestsTracing: { + enabled: true + } + httpLogs: { + fileSystem: { + enabled: true + retentionInDays: 1 + retentionInMb: 35 + } + } + } + diagnosticSettings: [ + { + workspaceResourceId: logAnalyticsWorkspaceResourceId + metricCategories: [ + { + category: 'AllMetrics' + } + ] + } + ] + httpsOnly: true + publicNetworkAccess: 'Enabled' + authSettingV2Configuration:{ + globalValidation: { + requireAuthentication: true + unauthenticatedClientAction: 'RedirectToLoginPage' + redirectToProvider: 'azureactivedirectory' + } + identityProviders: { + azureActiveDirectory: { + enabled: true + registration: { + clientId: authProvider.clientId + clientSecretSettingName: 'AUTH_CLIENT_SECRET' + openIdIssuer: authProvider.openIdIssuer + } + validation: { + defaultAuthorizationPolicy: { + allowedApplications: [] + } + } + } + } + login: { + tokenStore: { + enabled: true + } + } + } + appSettingsKeyValuePairs: { + APPINSIGHTS_INSTRUMENTATIONKEY: appInsights.properties.InstrumentationKey + APPINSIGHTS_PROFILERFEATURE_VERSION: '1.0.0' + APPINSIGHTS_SNAPSHOTFEATURE_VERSION: '1.0.0' + APPLICATIONINSIGHTS_CONFIGURATION_CONTENT: '' + APPLICATIONINSIGHTS_CONNECTION_STRING: appInsights.properties.ConnectionString + ApplicationInsightsAgent_EXTENSION_VERSION: '~3' + AUTH_CLIENT_SECRET: '@Microsoft.KeyVault(VaultName=${keyVault.name};SecretName=${authProvider.clientSecretName})' // NOTE: This secret should be created in Key Vault with the name provided in authProvider.clientSecretName. + AZURE_CLIENT_ID: userAssignedIdentity.properties.clientId // NOTE: This is the client ID of the managed identity, not the Entra application, and is needed for the App Service to access the Cosmos DB account. + AZURE_COSMOSDB_ACCOUNT: cosmosDbConfiguration.account + AZURE_COSMOSDB_CONVERSATIONS_CONTAINER: cosmosDbConfiguration.container + AZURE_COSMOSDB_DATABASE: cosmosDbConfiguration.database + AZURE_COSMOSDB_MONGO_VCORE_CONNECTION_STRING: '' + AZURE_COSMOSDB_MONGO_VCORE_CONTAINER: '' + AZURE_COSMOSDB_MONGO_VCORE_CONTENT_COLUMNS: 'content' + AZURE_COSMOSDB_MONGO_VCORE_DATABASE: '' + AZURE_COSMOSDB_MONGO_VCORE_FILENAME_COLUMN: 'filepath' + AZURE_COSMOSDB_MONGO_VCORE_INDEX: '' + AZURE_COSMOSDB_MONGO_VCORE_TITLE_COLUMN: 'title' + AZURE_COSMOSDB_MONGO_VCORE_URL_COLUMN: 'url' + AZURE_COSMOSDB_MONGO_VCORE_VECTOR_COLUMNS: 'contentVector' + AZURE_OPENAI_EMBEDDING_ENDPOINT: '' + AZURE_OPENAI_EMBEDDING_KEY: '' + AZURE_OPENAI_EMBEDDING_NAME: openAIConfiguration.embeddingModelDeploymentName + AZURE_OPENAI_ENDPOINT: openAIConfiguration.endpoint + AZURE_OPENAI_KEY: '' + AZURE_OPENAI_MAX_TOKENS: '800' + AZURE_OPENAI_MODEL: openAIConfiguration.gptModelName + AZURE_OPENAI_MODEL_NAME: openAIConfiguration.gptModelDeploymentName + AZURE_OPENAI_RESOURCE: openAIConfiguration.name + AZURE_OPENAI_STOP_SEQUENCE: '' + AZURE_OPENAI_SYSTEM_MESSAGE: 'You are an AI assistant that helps people find information.' + AZURE_OPENAI_TEMPERATURE: '0.7' + AZURE_OPENAI_TOP_P: '0.95' + AZURE_SEARCH_CONTENT_COLUMNS: 'content' + AZURE_SEARCH_ENABLE_IN_DOMAIN: 'true' + AZURE_SEARCH_FILENAME_COLUMN: 'filepath' + AZURE_SEARCH_INDEX: searchServiceConfiguration.indexName + AZURE_SEARCH_KEY: '' + AZURE_SEARCH_PERMITTED_GROUPS_COLUMN: '' + AZURE_SEARCH_QUERY_TYPE: 'vector_simple_hybrid' + AZURE_SEARCH_SEMANTIC_SEARCH_CONFIG: 'azureml-default' + AZURE_SEARCH_SERVICE: searchServiceConfiguration.name + AZURE_SEARCH_STRICTNESS: '3' + AZURE_SEARCH_TITLE_COLUMN: 'title' + AZURE_SEARCH_TOP_K: '5' + AZURE_SEARCH_URL_COLUMN: 'url' + AZURE_SEARCH_USE_SEMANTIC_SEARCH: 'true' + AZURE_SEARCH_VECTOR_COLUMNS: 'contentVector' + DATASOURCE_TYPE: 'AzureCognitiveSearch' + DiagnosticServices_EXTENSION_VERSION: '~3' + ELASTICSEARCH_CONTENT_COLUMNS: '' + ELASTICSEARCH_EMBEDDING_MODEL_ID: '' + ELASTICSEARCH_ENABLE_IN_DOMAIN: 'true' + ELASTICSEARCH_ENCODED_API_KEY: '' + ELASTICSEARCH_ENDPOINT: '' + ELASTICSEARCH_FILENAME_COLUMN: 'filepath' + ELASTICSEARCH_INDEX: '' + ELASTICSEARCH_QUERY_TYPE: '' + ELASTICSEARCH_STRICTNESS: '3' + ELASTICSEARCH_TITLE_COLUMN: 'title' + ELASTICSEARCH_TOP_K: '5' + ELASTICSEARCH_URL_COLUMN: 'url' + ELASTICSEARCH_VECTOR_COLUMNS: 'contentVector' + InstrumentationEngine_EXTENSION_VERSION: 'disabled' + MONGODB_APP_NAME: '' + MONGODB_COLLECTION_NAME: '' + MONGODB_CONTENT_COLUMNS: '' + MONGODB_DATABASE_NAME: '' + MONGODB_ENABLE_IN_DOMAIN: 'true' + MONGODB_ENDPOINT: '' + MONGODB_FILENAME_COLUMN: '' + MONGODB_INDEX_NAME: '' + MONGODB_PASSWORD: '' + MONGODB_STRICTNESS: '3' + MONGODB_TITLE_COLUMN: '' + MONGODB_TOP_K: '5' + MONGODB_URL_COLUMN: '' + MONGODB_USERNAME: '' + MONGODB_VECTOR_COLUMNS: '' + SCM_DO_BUILD_DURING_DEPLOYMENT: 'true' + SnapshotDebugger_EXTENSION_VERSION: 'disabled' + XDT_MicrosoftApplicationInsights_BaseExtensions: 'disabled' + XDT_MicrosoftApplicationInsights_Mode: 'recommended' + XDT_MicrosoftApplicationInsights_PreemptSdk: 'disabled' + } + siteConfig: { + alwaysOn: true + ftpsState: 'FtpsOnly' + linuxFxVersion: 'DOCKER|${imagePath}:${imageTag}' + } + } +} + +output resourceId string = appService.outputs.resourceId +output name string = appService.outputs.name +output uri string = 'https://${appService.outputs.defaultHostname}' +output appServicePlanResourceId string = appServicePlan.outputs.resourceId +output appServicePlanName string = appServicePlan.outputs.name + +@export() +@description('Values for setting up authentication with Entra provider.') +type authIdentityProvider = { + @description('Required. The client/application ID of the Entra application.') + clientId: string + + @description('Required. The secret name of the Azure Active Directory application secret stored in Key Vault.') + clientSecretName: string + + @description('Required. The OpenID issuer of the Entra application.') + openIdIssuer: string +} + +@export() +@description('Values for setting up Cosmos DB configuration.') +type cosmosDbConfig = { + @description('Required. The name of the Cosmos DB account.') + account: string + + @description('Required. The name of the Cosmos DB database.') + database: string + + @description('Required. The name of the Cosmos DB container.') + container: string +} + +@export() +@description('Values for setting up Azure Search configuration.') +type searchServiceConfig = { + @description('Required. The name of the Azure Search service.') + name: string + + @description('Required. The name of the Azure Search index.') + indexName: string +} + +@export() +@description('Values for setting up OpenAI configuration.') +type openAIConfig = { + @description('Required. The name of the OpenAI resource.') + name: string + + @description('Required. The endpoint of the OpenAI resource.') + endpoint: string + + @description('Required. The name of the OpenAI embedding model deployment.') + embeddingModelDeploymentName: string + + @description('Required. The name of the OpenAI GPT model (gpt-4o).') + gptModelName: string + + @description('Required. The name of the OpenAI GPT model deployment.') + gptModelDeploymentName: string +} diff --git a/infra/modules/cognitive-services/main.bicep b/infra/modules/cognitive-services/main.bicep index 32c7372..491a1ed 100644 --- a/infra/modules/cognitive-services/main.bicep +++ b/infra/modules/cognitive-services/main.bicep @@ -18,6 +18,9 @@ param userObjectId string @description('Optional. Tags to be applied to the resources.') param tags object = {} +@description('Optional. Array of identity principals to assign app-focused access.') +param principalIds string[] = [] + @description('Resource ID of the virtual network to link the private DNS zones.') param virtualNetworkResourceId string @@ -77,6 +80,32 @@ module openAiPrivateDnsZone 'br/public:avm/res/network/private-dns-zone:0.7.0' = } } +var roleAssignmentsForServicePrincipals = [ + for id in principalIds: { + principalId: id + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Cognitive Services OpenAI User' + } +] + +var allRoleAssignments = concat(empty(userObjectId) ? [] : [ + { + principalId: userObjectId + principalType: 'User' + roleDefinitionIdOrName: 'Cognitive Services OpenAI Contributor' + } + { + principalId: userObjectId + principalType: 'User' + roleDefinitionIdOrName: 'Cognitive Services Contributor' + } + { + principalId: userObjectId + principalType: 'User' + roleDefinitionIdOrName: 'Cognitive Services User' + } +], roleAssignmentsForServicePrincipals) + module aiServices 'service.bicep' = { name: take('${name}-ai-services-deployment', 64) #disable-next-line no-unnecessary-dependson @@ -96,23 +125,7 @@ module aiServices 'service.bicep' = { logAnalyticsWorkspaceResourceId: logAnalyticsWorkspaceResourceId aiModelDeployments: aiModelDeployments - roleAssignments: empty(userObjectId) ? [] : [ - { - principalId: userObjectId - principalType: 'User' - roleDefinitionIdOrName: 'Cognitive Services OpenAI Contributor' - } - { - principalId: userObjectId - principalType: 'User' - roleDefinitionIdOrName: 'Cognitive Services Contributor' - } - { - principalId: userObjectId - principalType: 'User' - roleDefinitionIdOrName: 'Cognitive Services User' - } - ] + roleAssignments: allRoleAssignments tags: tags } } @@ -132,6 +145,7 @@ module contentSafety 'service.bicep' = if (contentSafetyEnabled) { cognitiveServicesPrivateDnsZone.outputs.resourceId ]: [] logAnalyticsWorkspaceResourceId: logAnalyticsWorkspaceResourceId + roleAssignments: allRoleAssignments tags: tags } } @@ -152,6 +166,7 @@ module vision 'service.bicep' = if (visionEnabled) { cognitiveServicesPrivateDnsZone.outputs.resourceId ] : [] logAnalyticsWorkspaceResourceId: logAnalyticsWorkspaceResourceId + roleAssignments: allRoleAssignments tags: tags } } @@ -172,6 +187,7 @@ module language 'service.bicep' = if (languageEnabled) { cognitiveServicesPrivateDnsZone.outputs.resourceId ] : [] logAnalyticsWorkspaceResourceId: logAnalyticsWorkspaceResourceId + roleAssignments: allRoleAssignments tags: tags } } @@ -191,6 +207,7 @@ module speech 'service.bicep' = if (speechEnabled) { cognitiveServicesPrivateDnsZone.outputs.resourceId ] : [] logAnalyticsWorkspaceResourceId: logAnalyticsWorkspaceResourceId + roleAssignments: allRoleAssignments tags: tags } } @@ -211,6 +228,7 @@ module translator 'service.bicep' = if (translatorEnabled) { cognitiveServicesPrivateDnsZone.outputs.resourceId ] : [] logAnalyticsWorkspaceResourceId: logAnalyticsWorkspaceResourceId + roleAssignments: allRoleAssignments tags: tags } } @@ -229,6 +247,7 @@ module documentIntelligence 'service.bicep' = if (documentIntelligenceEnabled) { cognitiveServicesPrivateDnsZone.outputs.resourceId ] : [] logAnalyticsWorkspaceResourceId: logAnalyticsWorkspaceResourceId + roleAssignments: allRoleAssignments networkAcls: networkAcls tags: tags } diff --git a/infra/modules/cosmosDb.bicep b/infra/modules/cosmosDb.bicep index d4f30c7..1875dc1 100644 --- a/infra/modules/cosmosDb.bicep +++ b/infra/modules/cosmosDb.bicep @@ -22,6 +22,9 @@ param networkIsolation bool = true @description('Optional. Array of role assignments to create.') param roleAssignments roleAssignmentType[]? +@description('Optional. List of principal IDs for the custom read/write SQL role assignment.') +param sqlRoleAssignmentsPrincipalIds string [] = [] + @description('Optional. List of Cosmos DB databases to deploy.') param databases sqlDatabaseType[]? @@ -40,7 +43,7 @@ module privateDnsZone 'br/public:avm/res/network/private-dns-zone:0.7.0' = if (n var nameFormatted = toLower(name) -module cosmosDb 'br/public:avm/res/document-db/database-account:0.11.0' = { +module cosmosDb 'br/public:avm/res/document-db/database-account:0.13.0' = { name: take('${nameFormatted}-cosmosdb-deployment', 64) #disable-next-line no-unnecessary-dependson dependsOn: [privateDnsZone] // required due to optional flags that could change dependency @@ -76,6 +79,19 @@ module cosmosDb 'br/public:avm/res/document-db/database-account:0.11.0' = { ] : [] sqlDatabases: databases roleAssignments: roleAssignments + sqlRoleDefinitions: [ + { + name: guid(resourceGroup().id, nameFormatted, 'custom-sql-role') + roleType: 'CustomRole' + roleName: 'Cosmos DB Data Reader Writer' + dataAction:[ + 'Microsoft.DocumentDB/databaseAccounts/readMetadata' + 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/items/*' + 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/*' + ] + } + ] + sqlRoleAssignmentsPrincipalIds: sqlRoleAssignmentsPrincipalIds tags: tags } } diff --git a/infra/modules/customTypes.bicep b/infra/modules/customTypes.bicep index 6fe8473..7e35428 100644 --- a/infra/modules/customTypes.bicep +++ b/infra/modules/customTypes.bicep @@ -311,5 +311,4 @@ type deploymentsType = { versionUpgradeOption: string? } - import { diagnosticSettingFullType } from 'br/public:avm/utl/types/avm-common-types:0.5.1' diff --git a/infra/modules/keyvault.bicep b/infra/modules/keyvault.bicep index 1a0f1fe..ed2b60b 100644 --- a/infra/modules/keyvault.bicep +++ b/infra/modules/keyvault.bicep @@ -19,9 +19,11 @@ param logAnalyticsWorkspaceResourceId string @description('Specifies whether network isolation is enabled. This will create a private endpoint for the Key Vault and link the private DNS zone.') param networkIsolation bool = true -@description('Specifies the object id of a Microsoft Entra ID user. In general, this the object id of the system administrator who deploys the Azure resources. This defaults to the deploying user.') -param userObjectId string +@description('Optional. Array of role assignments to create.') +param roleAssignments roleAssignmentType[]? +@description('Optional. Array of secrets to create in the Key Vault.') +param secrets secretType[]? module privateDnsZone 'br/public:avm/res/network/private-dns-zone:0.7.0' = if (networkIsolation) { name: 'private-dns-keyvault-deployment' @@ -38,7 +40,7 @@ module privateDnsZone 'br/public:avm/res/network/private-dns-zone:0.7.0' = if (n var nameFormatted = take(toLower(name), 24) -module keyvault 'br/public:avm/res/key-vault/vault:0.11.0' = { +module keyvault 'br/public:avm/res/key-vault/vault:0.12.1' = { name: take('${nameFormatted}-keyvault-deployment', 64) #disable-next-line no-unnecessary-dependson dependsOn: [privateDnsZone] // required due to optional flags that could change dependency @@ -53,11 +55,11 @@ module keyvault 'br/public:avm/res/key-vault/vault:0.11.0' = { enableVaultForDeployment: true enableVaultForDiskEncryption: true enableVaultForTemplateDeployment: true - enablePurgeProtection: true + enablePurgeProtection: false enableRbacAuthorization: true enableSoftDelete: true softDeleteRetentionInDays: 7 - diagnosticSettings:[ + diagnosticSettings: [ { workspaceResourceId: logAnalyticsWorkspaceResourceId } @@ -75,15 +77,13 @@ module keyvault 'br/public:avm/res/key-vault/vault:0.11.0' = { subnetResourceId: virtualNetworkSubnetResourceId } ] : [] - roleAssignments: empty(userObjectId) ? [] : [ - { - principalId: userObjectId - principalType: 'User' - roleDefinitionIdOrName: 'Key Vault Secrets User' - } - ] + roleAssignments: roleAssignments + secrets: secrets } } +import { roleAssignmentType } from 'br/public:avm/utl/types/avm-common-types:0.5.1' +import { secretType } from 'br/public:avm/res/key-vault/vault:0.12.1' + output resourceId string = keyvault.outputs.resourceId output name string = keyvault.outputs.name diff --git a/infra/modules/storageAccount.bicep b/infra/modules/storageAccount.bicep index dbca770..a7546a9 100644 --- a/infra/modules/storageAccount.bicep +++ b/infra/modules/storageAccount.bicep @@ -66,6 +66,7 @@ module storageAccount 'br/public:avm/res/storage/storage-account:0.17.0' = { minimumTlsVersion: 'TLS1_2' networkAcls: { defaultAction: 'Allow' + bypass: 'AzureServices' } supportsHttpsTrafficOnly: true diagnosticSettings: [ diff --git a/infra/modules/virtualMachine.bicep b/infra/modules/virtualMachine.bicep index db1b72b..eae078e 100644 --- a/infra/modules/virtualMachine.bicep +++ b/infra/modules/virtualMachine.bicep @@ -129,6 +129,9 @@ resource virtualMachine 'Microsoft.Compute/virtualMachines@2021-11-01' = { name: vmName location: location tags: tags + identity: { + type: 'SystemAssigned' + } properties: { hardwareProfile: { vmSize: vmSize @@ -399,3 +402,4 @@ resource virtualMachineAdministratorLoginUserRoleAssignment 'Microsoft.Authoriza output name string = virtualMachine.name output id string = virtualMachine.id +output principalId string = virtualMachine.identity.principalId diff --git a/infra/modules/virtualNetwork.bicep b/infra/modules/virtualNetwork.bicep index 2070101..f0c8759 100644 --- a/infra/modules/virtualNetwork.bicep +++ b/infra/modules/virtualNetwork.bicep @@ -1,181 +1,29 @@ -// Parameters -@description('Specifies the name of the virtual network.') -param virtualNetworkName string - -@description('Specifies the address prefixes of the virtual network.') -param virtualNetworkAddressPrefixes string = '10.0.0.0/8' - -@description('Specifies the name of the subnet which contains the virtual machine.') -param vmSubnetName string = 'VmSubnet' - -@description('Specifies the address prefix of the subnet which contains the virtual machine.') -param vmSubnetAddressPrefix string = '10.3.1.0/24' - -@description('Specifies the name of the network security group associated to the subnet hosting the virtual machine.') -param vmSubnetNsgName string = 'VmSubnetNsg' - -@description('Specifies the Bastion subnet IP prefix. This prefix must be within vnet IP prefix address space.') -param bastionSubnetAddressPrefix string = '10.3.2.0/24' - -@description('Specifies the name of the network security group associated to the subnet hosting Azure Bastion.') -param bastionSubnetNsgName string = 'AzureBastionNsg' - -@description('Specifies whether Azure Bastion should be created.') -param bastionHostEnabled bool = true - -@description('Specifies the name of the Azure Bastion resource.') -param bastionHostName string - -@description('Enable/Disable Copy/Paste feature of the Bastion Host resource.') -param bastionHostDisableCopyPaste bool = true - -@description('Enable/Disable File Copy feature of the Bastion Host resource.') -param bastionHostEnableFileCopy bool = true - -@description('Enable/Disable IP Connect feature of the Bastion Host resource.') -param bastionHostEnableIpConnect bool = true - -@description('Enable/Disable Shareable Link of the Bastion Host resource.') -param bastionHostEnableShareableLink bool = true - -@description('Enable/Disable Tunneling feature of the Bastion Host resource.') -param bastionHostEnableTunneling bool = true - -@description('Specifies the name of the Azure Public IP Address used by the Azure Bastion Host.') -param bastionPublicIpAddressName string - -@description('Specifies the name of the Azure Bastion Host SKU.') -param bastionHostSkuName string = 'Standard' - -@description('Specifies the name of the Azure NAT Gateway.') -param natGatewayName string - -@description('Specifies a list of availability zones denoting the zone in which Nat Gateway should be deployed.') -param natGatewayZones array = [] - -@description('Specifies the number of Public IPs to create for the Azure NAT Gateway.') -param natGatewayPublicIps int = 1 - -@description('Specifies the idle timeout in minutes for the Azure NAT Gateway.') -param natGatewayIdleTimeoutMins int = 30 +@description('Specifies the name used to name networking Azure resources.') +param resourceToken string @description('Optional IP address to allow access throught Bastion NSG. If not specified, all IP addresses are allowed.') param allowedIpAddress string = '' @description('Specifies the resource id of the Log Analytics workspace.') -param workspaceId string +param logAnalyticsWorkspaceId string @description('Specifies the location.') -param location string = resourceGroup().location +param location string @description('Specifies the resource tags.') -param tags object +param tags object = {} -// Variables -var diagnosticSettingsName = 'diagnosticSettings' -var nsgLogCategories = [ - 'NetworkSecurityGroupEvent' - 'NetworkSecurityGroupRuleCounter' -] -var nsgLogs = [for category in nsgLogCategories: { - category: category - enabled: true - retentionPolicy: { - enabled: true - days: 0 - } -}] -var vnetLogCategories = [ - 'VMProtectionAlerts' -] -var vnetMetricCategories = [ - 'AllMetrics' -] -var vnetLogs = [for category in vnetLogCategories: { - category: category - enabled: true - retentionPolicy: { - enabled: true - days: 0 - } -}] -var vnetMetrics = [for category in vnetMetricCategories: { - category: category - enabled: true - retentionPolicy: { - enabled: true - days: 0 - } -}] -var bastionLogCategories = [ - 'BastionAuditLogs' -] -var bastionMetricCategories = [ - 'AllMetrics' -] -var bastionLogs = [for category in bastionLogCategories: { - category: category - enabled: true - retentionPolicy: { - enabled: true - days: 0 - } -}] -var bastionMetrics = [for category in bastionMetricCategories: { - category: category - enabled: true - retentionPolicy: { - enabled: true - days: 0 - } -}] var bastionSubnetName = 'AzureBastionSubnet' - -// Virtual Network -resource vnet 'Microsoft.Network/virtualNetworks@2024-03-01' = { - name: virtualNetworkName - location: location - tags: tags - properties: { - addressSpace: { - addressPrefixes: [ - virtualNetworkAddressPrefixes - ] - } - subnets: [ - { - name: vmSubnetName - properties: { - addressPrefix: vmSubnetAddressPrefix - networkSecurityGroup: { - id: vmSubnetNsg.id - } - privateEndpointNetworkPolicies: 'Disabled' - privateLinkServiceNetworkPolicies: 'Disabled' - natGateway: { - id: natGateway.id - } - } - } - { - name: bastionSubnetName - properties: { - addressPrefix: bastionSubnetAddressPrefix - networkSecurityGroup: { - id: bastionSubnetNsg.id - } - } - } - ] - } -} - -// Network Security Groups -resource bastionSubnetNsg 'Microsoft.Network/networkSecurityGroups@2023-04-01' = if (bastionHostEnabled) { - name: bastionSubnetNsgName - location: location - tags: tags - properties: { +var defaultSubnetName = 'snet-default' +var appSubnetName = 'snet-web-apps' + +module bastionNetworkSecurityGroup 'br/public:avm/res/network/network-security-group:0.5.1' = { + name: take('${resourceToken}-bastion-nsg', 64) + params: { + name: 'nsg-${bastionSubnetName}' + location: location + tags: tags + diagnosticSettings: [{ workspaceResourceId: logAnalyticsWorkspaceId }] securityRules: [ { name: 'AllowHttpsInBound' @@ -323,132 +171,96 @@ resource bastionSubnetNsg 'Microsoft.Network/networkSecurityGroups@2023-04-01' = } } -resource vmSubnetNsg 'Microsoft.Network/networkSecurityGroups@2023-04-01' = { - name: vmSubnetNsgName - location: location - tags: tags - properties: { +module defaultSubnetNetworkSecurityGroup 'br/public:avm/res/network/network-security-group:0.5.1' = { + name: take('${resourceToken}-default-nsg', 64) + params: { + name: 'nsg-${defaultSubnetName}' + location: location + tags: tags + diagnosticSettings: [{ workspaceResourceId: logAnalyticsWorkspaceId }] securityRules: [] } } -// NAT Gateway -resource natGatewayPublicIp 'Microsoft.Network/publicIPAddresses@2023-04-01' = [for i in range(0, natGatewayPublicIps): { - name: natGatewayPublicIps == 1 ? '${natGatewayName}PublicIp' : '${natGatewayName}PublicIp${i + 1}' - location: location - sku: { - name: 'Standard' - } - zones: !empty(natGatewayZones) ? natGatewayZones : [] - properties: { - publicIPAllocationMethod: 'Static' - } -}] - -resource natGateway 'Microsoft.Network/natGateways@2024-03-01' = { - name: natGatewayName - location: location - sku: { - name: 'Standard' - } - zones: !empty(natGatewayZones) ? natGatewayZones : [] - properties: { - publicIpAddresses: [for i in range(0, natGatewayPublicIps): { - id: natGatewayPublicIp[i].id - }] - idleTimeoutInMinutes: natGatewayIdleTimeoutMins - } - dependsOn: [ - natGatewayPublicIp - ] -} - -// Azure Bastion Host -resource bastionPublicIpAddress 'Microsoft.Network/publicIPAddresses@2023-04-01' = if (bastionHostEnabled) { - name: bastionPublicIpAddressName - location: location - tags: tags - sku: { - name: 'Standard' - } - properties: { - publicIPAllocationMethod: 'Static' +module appSubnetNetworkSecurityGroup 'br/public:avm/res/network/network-security-group:0.5.1' = { + name: take('${resourceToken}-app-nsg', 64) + params: { + name: 'nsg-${appSubnetName}' + location: location + tags: tags + diagnosticSettings: [{ workspaceResourceId: logAnalyticsWorkspaceId }] + securityRules: [] } } -resource bastionHost 'Microsoft.Network/bastionHosts@2023-04-01' = if (bastionHostEnabled) { - name: bastionHostName - location: location - tags: tags - sku: { - name: bastionHostSkuName - } - properties: { - disableCopyPaste: bastionHostDisableCopyPaste - enableFileCopy: bastionHostEnableFileCopy - enableIpConnect: bastionHostEnableIpConnect - enableShareableLink: bastionHostEnableShareableLink - enableTunneling: bastionHostEnableTunneling - ipConfigurations: [ +module virtualNetwork 'br/public:avm/res/network/virtual-network:0.7.0' = { + name: take('${resourceToken}-vnet', 64) + params: { + name: 'vnet-${resourceToken}' + location: location + addressPrefixes: ['10.0.0.0/8'] + diagnosticSettings:[ + { + workspaceResourceId: logAnalyticsWorkspaceId + logCategoriesAndGroups: [{category: 'VMProtectionAlerts'}] + metricCategories:[ {category: 'AllMetrics'}] + }] + subnets: [ { - name: 'IpConf' - properties: { - subnet: { - id: '${vnet.id}/subnets/${bastionSubnetName}' - } - publicIPAddress: { - id: bastionPublicIpAddress.id - } - } + name: defaultSubnetName + addressPrefix: '10.3.1.0/24' + privateEndpointNetworkPolicies: 'Disabled' + privateLinkServiceNetworkPolicies: 'Disabled' + networkSecurityGroupResourceId: defaultSubnetNetworkSecurityGroup.outputs.resourceId + } + { + name: bastionSubnetName + addressPrefix: '10.3.2.0/24' + networkSecurityGroupResourceId: bastionNetworkSecurityGroup.outputs.resourceId + } + { + name: appSubnetName + addressPrefix: '10.3.3.0/24' + networkSecurityGroupResourceId: appSubnetNetworkSecurityGroup.outputs.resourceId + delegation: 'Microsoft.Web/serverfarms' } ] + tags: tags } } -// Diagnostic Settings -resource vmSubnetNsgDiagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = { - name: diagnosticSettingsName - scope: vmSubnetNsg - properties: { - workspaceId: workspaceId - logs: nsgLogs +module bastionHost 'br/public:avm/res/network/bastion-host:0.6.1' = { + name: take('${resourceToken}-bastion', 64) + params: { + name: 'bas-${resourceToken}' + location: location + skuName: 'Standard' + virtualNetworkResourceId: virtualNetwork.outputs.resourceId + diagnosticSettings: [{ workspaceResourceId: logAnalyticsWorkspaceId }] + tags: tags + disableCopyPaste: false + enableFileCopy: true + enableIpConnect: true + enableShareableLink: true + publicIPAddressObject: { + name: 'pip-bas-${resourceToken}' + skuName: 'Standard' + publicIPAllocationMethod: 'Static' + diagnosticSettings: [{ workspaceResourceId: logAnalyticsWorkspaceId }] + tags: tags + } } } -resource bastionSubnetNsgDiagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (bastionHostEnabled) { - name: diagnosticSettingsName - scope: bastionSubnetNsg - properties: { - workspaceId: workspaceId - logs: nsgLogs - } -} +output resourceId string = virtualNetwork.outputs.resourceId +output name string = virtualNetwork.outputs.name +output bastionName string = bastionHost.outputs.name -resource vnetDiagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = { - name: diagnosticSettingsName - scope: vnet - properties: { - workspaceId: workspaceId - logs: vnetLogs - metrics: vnetMetrics - } -} +output defaultSubnetName string = virtualNetwork.outputs.subnetNames[0] +output defaultSubnetResourceId string = virtualNetwork.outputs.subnetResourceIds[0] -resource bastionDiagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (bastionHostEnabled) { - name: diagnosticSettingsName - scope: bastionHost - properties: { - workspaceId: workspaceId - logs: bastionLogs - metrics: bastionMetrics - } -} +output bastionSubnetName string = virtualNetwork.outputs.subnetNames[1] +output bastionSubnetResourceId string = virtualNetwork.outputs.subnetResourceIds[1] -// Outputs -output virtualNetworkId string = vnet.id -output virtualNetworkName string = vnet.name -output vmSubnetId string = resourceId('Microsoft.Network/virtualNetworks/subnets', vnet.name, vmSubnetName) -output bastionSubnetId string = resourceId('Microsoft.Network/virtualNetworks/subnets', vnet.name, bastionSubnetName) -output vmSubnetName string = vmSubnetName -output bastionSubnetName string = bastionSubnetName -output bastionName string = bastionHost.name +output appSubnetName string = virtualNetwork.outputs.subnetNames[2] +output appSubnetResourceId string = virtualNetwork.outputs.subnetResourceIds[2] diff --git a/infra/modules/vmscriptsetup.bicep b/infra/modules/vmscriptsetup.bicep new file mode 100644 index 0000000..273da35 --- /dev/null +++ b/infra/modules/vmscriptsetup.bicep @@ -0,0 +1,101 @@ + +@description('The name of the existing Azure AI Search service to be referenced.') +param aiSearchName string + +@description('The name of the existing Azure Cognitive Services account to be referenced.') +param cognitiveServicesName string + +@description('Specifies the AI embedding model to use for the AI Foundry deployment. This is the model used for text embeddings in AI Foundry. NOTE: Any adjustments to this parameter\'s values must also be made on the aiDeploymentsLocation metadata in the main.bicep file.') +param aiEmbeddingModelDeployment modelDeploymentType + +@description('Specifies whether network isolation is enabled. When true, Foundry and related components will be deployed, network access parameters will be set to Disabled.') +param networkIsolation bool = true + +@description('Principal ID (objectId) of the VM’s managed identity') +param virtualMachinePrincipalId string = '' + +@description('The name of the virtual machine where the script will be executed.') +param vmName string + +@description('The location for the resources.') +param location string = resourceGroup().location + +@description('The URL of the script to be executed on the virtual machine.') +param installtionScript string = 'https://raw.githubusercontent.com/microsoft/Deploy-Your-AI-Application-In-Production/main/scripts/install_python.ps1' + +resource vm 'Microsoft.Compute/virtualMachines@2023-03-01' existing = if (networkIsolation) { + name: vmName +} + +resource customScriptExt 'Microsoft.Compute/virtualMachines/extensions@2023-03-01' = if (networkIsolation) { + name: 'CustomScriptExtension' + parent: vm + location: location + properties: { + publisher: 'Microsoft.Compute' + type: 'CustomScriptExtension' + typeHandlerVersion: '1.10' + autoUpgradeMinorVersion: true + settings: { + fileUris: [ + installtionScript + ] + commandToExecute: 'powershell -ExecutionPolicy Bypass -File install_python.ps1' + } + } + dependsOn: [searchIndexRoleAssignment, searchServiceRoleAssignment, roleAssignment] +} + +resource cognitiveServicesRes 'Microsoft.CognitiveServices/accounts@2025-04-01-preview' existing = { + name: cognitiveServicesName +} + +resource aiSearchResource 'Microsoft.Search/searchServices@2023-11-01' existing = { + name: aiSearchName +} + +// Search Index Data Contributor role ID +var searchIndexContributorRoleId = subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + '8ebe5a00-799e-43f5-93ac-243d3dce84a7' +) + +var searchServiceContributorRoleId = subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + '7ca78c08-252a-4471-8644-bb5ff32d4ba0' +) + +resource searchIndexRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = if(networkIsolation) { + name: guid(aiSearchResource.id, virtualMachinePrincipalId, 'SearchIndexDataContributor') + scope: aiSearchResource + properties: { + roleDefinitionId: searchIndexContributorRoleId + principalId: virtualMachinePrincipalId + principalType: 'ServicePrincipal' + } +} + +resource searchServiceRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = if(networkIsolation) { + name: guid(aiSearchResource.id, virtualMachinePrincipalId, 'SearchServiceContributor') + scope: aiSearchResource + properties: { + roleDefinitionId: searchServiceContributorRoleId + principalId: virtualMachinePrincipalId + principalType: 'ServicePrincipal' + } +} + +@description('Role definition ID or name') +var openAiUserRole = 'Cognitive Services OpenAI User' + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = if(networkIsolation) { + name: guid(cognitiveServicesRes.id, virtualMachinePrincipalId, openAiUserRole) + scope: cognitiveServicesRes + properties: { + principalId: virtualMachinePrincipalId + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e0bd9bd-7b93-4f28-af87-19fc36ad61bd') // OpenAI User Role + principalType: 'ServicePrincipal' + } +} + +import { modelDeploymentType } from 'customTypes.bicep' diff --git a/requirements-dev.txt b/requirements-dev.txt new file mode 100644 index 0000000..feb186e --- /dev/null +++ b/requirements-dev.txt @@ -0,0 +1 @@ +-r requirements.txt \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..dc7f8f9 --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +azure-identity==1.15.0 diff --git a/scripts/auth_init.ps1 b/scripts/auth_init.ps1 new file mode 100644 index 0000000..cfea366 --- /dev/null +++ b/scripts/auth_init.ps1 @@ -0,0 +1,16 @@ +. ./scripts/loadenv.ps1 + +if (-not $env:AZURE_APP_SAMPLE_ENABLED -or $env:AZURE_APP_SAMPLE_ENABLED -eq "false") { + Write-Host "AZURE_APP_SAMPLE_ENABLED is false. Exiting auth_init script." + exit +} + +$venvPythonPath = "./.venv/scripts/python.exe" +if (Test-Path -Path "/usr") { + # fallback to Linux venv path + $venvPythonPath = "./.venv/bin/python" +} + +Write-Host 'Running "auth_init.py"' +$appId = $env:AZURE_AUTH_APP_ID ?? "no-id" +Start-Process -FilePath $venvPythonPath -ArgumentList "./scripts/auth_init.py --appid $appId" -Wait -NoNewWindow diff --git a/scripts/auth_init.py b/scripts/auth_init.py new file mode 100644 index 0000000..e833e7b --- /dev/null +++ b/scripts/auth_init.py @@ -0,0 +1,93 @@ +import argparse +import subprocess + +from azure.identity import AzureDeveloperCliCredential, DefaultAzureCredential +import urllib3 + +def get_auth_headers(credential): + return { + "Authorization": "Bearer " + + credential.get_token("https://graph.microsoft.com/.default").token + } + + +def check_for_application(credential, app_id): + resp = urllib3.request( + "GET", + f"https://graph.microsoft.com/v1.0/applications/{app_id}", + headers=get_auth_headers(credential), + ) + if resp.status != 200: + print("Application not found") + return False + return True + + +def create_application(credential): + resp = urllib3.request( + "POST", + "https://graph.microsoft.com/v1.0/applications", + headers=get_auth_headers(credential), + json={ + "displayName": "WebApp", + "signInAudience": "AzureADandPersonalMicrosoftAccount", + "web": { + "redirectUris": ["http://localhost:5000/.auth/login/aad/callback"], + "implicitGrantSettings": {"enableIdTokenIssuance": True}, + }, + }, + timeout=urllib3.Timeout(connect=10, read=10), + ) + + app_id = resp.json()["id"] + client_id = resp.json()["appId"] + + return app_id, client_id + + +def add_client_secret(credential, app_id): + resp = urllib3.request( + "POST", + f"https://graph.microsoft.com/v1.0/applications/{app_id}/addPassword", + headers=get_auth_headers(credential), + json={"passwordCredential": {"displayName": "WebAppSecret"}}, + timeout=urllib3.Timeout(connect=10, read=10), + ) + client_secret = resp.json()["secretText"] + return client_secret + + +def update_azd_env(name, val): + subprocess.run(f"azd env set {name} {val}", shell=True) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser( + description="Create an App Registration and client secret (if not already created)", + epilog="Example: auth_update.py", + ) + parser.add_argument( + "--appid", + required=False, + help="Optional. ID of registered application. If provided, this script just makes sure it exists.", + ) + args = parser.parse_args() + + credential = DefaultAzureCredential() # AzureDeveloperCliCredential() + + if args.appid and args.appid != "no-id": + print(f"Checking if application {args.appid} exists") + if check_for_application(credential, args.appid): + print("Application already exists, not creating new one.") + exit(0) + + print("Creating application registration") + app_id, client_id = create_application(credential) + + print(f"Adding client secret to {app_id}") + client_secret = add_client_secret(credential, app_id) + + print("Updating azd env with AZURE_AUTH_APP_ID, AZURE_AUTH_CLIENT_ID, AZURE_AUTH_CLIENT_SECRET") + update_azd_env("AZURE_AUTH_APP_ID", app_id) + update_azd_env("AZURE_AUTH_CLIENT_ID", client_id) + update_azd_env("AZURE_AUTH_CLIENT_SECRET", client_secret) diff --git a/scripts/auth_init.sh b/scripts/auth_init.sh new file mode 100755 index 0000000..b6da77b --- /dev/null +++ b/scripts/auth_init.sh @@ -0,0 +1,11 @@ + #!/bin/sh + +. ./scripts/loadenv.sh + +if [ "$AZURE_APP_SAMPLE_ENABLED" = "false" ]; then + echo "AZURE_APP_SAMPLE_ENABLED is false. Exiting auth_init script." + exit 1 +fi + +echo 'Running "auth_init.py"' +./.venv/bin/python ./scripts/auth_init.py --appid "$AZURE_AUTH_APP_ID" diff --git a/scripts/auth_update.py b/scripts/auth_update.py new file mode 100644 index 0000000..07ab82b --- /dev/null +++ b/scripts/auth_update.py @@ -0,0 +1,52 @@ +import argparse + +from azure.identity import DefaultAzureCredential, AzureDeveloperCliCredential +import urllib3 + + +def update_redirect_uris(credential, app_id, uri): + urllib3.request( + "PATCH", + f"https://graph.microsoft.com/v1.0/applications/{app_id}", + headers={ + "Authorization": "Bearer " + + credential.get_token("https://graph.microsoft.com/.default").token, + }, + json={ + "web": { + "redirectUris": [ + "http://localhost:5000/.auth/login/aad/callback", + f"{uri}/.auth/login/aad/callback", + ], + "implicitGrantSettings": { + "enableIdTokenIssuance": True, + "enableAccessTokenIssuance": False # Optional: can also be True + } + } + }, + ) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser( + description="Add a redirect URI to a registered application", + epilog="Example: auth_update.py --appid 123 --uri https://abc.azureservices.net", + ) + parser.add_argument( + "--appid", + required=False, + help="Required. ID of the application to update.", + ) + parser.add_argument( + "--uri", + required=False, + help="Required. URI of the deployed application.", + ) + args = parser.parse_args() + + credential = DefaultAzureCredential() + + print( + f"Updating application registration {args.appid} with redirect URI for {args.uri}" + ) + update_redirect_uris(credential, args.appid, args.uri) diff --git a/scripts/index_scripts/01_create_search_index.py b/scripts/index_scripts/01_create_search_index.py new file mode 100644 index 0000000..390deda --- /dev/null +++ b/scripts/index_scripts/01_create_search_index.py @@ -0,0 +1,72 @@ +from azure.identity import DefaultAzureCredential + +from azure.search.documents.indexes import SearchIndexClient +from azure.search.documents.indexes.models import ( + SimpleField, + SearchFieldDataType, + SearchField, + VectorSearch, + HnswAlgorithmConfiguration, + VectorSearchProfile, + SemanticConfiguration, + SemanticPrioritizedFields, + SemanticField, + SemanticSearch, + SearchIndex +) +import os + +index_name = "ai_app_index" + +search_endpoint = os.getenv("SEARCH_ENDPOINT") + +print(f"Creating search index at {search_endpoint} with index name {index_name}") + +# Create the search index +def create_search_index(): + search_credential = DefaultAzureCredential() + + # Create a search index + index_client = SearchIndexClient(endpoint=search_endpoint, credential=search_credential) + + fields = [ + SimpleField(name="id", type=SearchFieldDataType.String, key=True), + SimpleField(name="chunk_id", type=SearchFieldDataType.String), + SearchField(name="content", type=SearchFieldDataType.String), + SimpleField(name="sourceurl", type=SearchFieldDataType.String), + SearchField(name="contentVector", type=SearchFieldDataType.Collection(SearchFieldDataType.Single), vector_search_dimensions=1536, vector_search_profile_name="myHnswProfile") + ] + + # Configure the vector search configuration + vector_search = VectorSearch( + algorithms=[ + HnswAlgorithmConfiguration( + name="myHnsw" + ) + ], + profiles=[ + VectorSearchProfile( + name="myHnswProfile", + algorithm_configuration_name="myHnsw", + ) + ] + ) + + semantic_config = SemanticConfiguration( + name="my-semantic-config", + prioritized_fields=SemanticPrioritizedFields( + keywords_fields=[SemanticField(field_name="chunk_id")], + content_fields=[SemanticField(field_name="content")] + ) + ) + + # Create the semantic settings with the configuration + semantic_search = SemanticSearch(configurations=[semantic_config]) + + # Create the search index with the semantic settings + index = SearchIndex(name=index_name, fields=fields, + vector_search=vector_search, semantic_search=semantic_search) + result = index_client.create_or_update_index(index) + print(f' {result.name} created') + +create_search_index() diff --git a/scripts/index_scripts/02_process_data.py b/scripts/index_scripts/02_process_data.py new file mode 100644 index 0000000..0133cba --- /dev/null +++ b/scripts/index_scripts/02_process_data.py @@ -0,0 +1,179 @@ +from azure.identity import DefaultAzureCredential, get_bearer_token_provider +from openai import AzureOpenAI +import re +import time +from pypdf import PdfReader +from io import BytesIO +from azure.search.documents import SearchClient +from azure.search.documents.indexes import SearchIndexClient +import os +import requests + +search_endpoint = os.getenv("SEARCH_ENDPOINT") +openai_endpoint = os.getenv("OPEN_AI_ENDPOINT_URL") +embedding_model_name = os.getenv("EMBEDDING_MODEL_NAME") +embedding_model_api_version = os.getenv("EMBEDDING_MODEL_API_VERSION") +use_local_files = (os.getenv("USE_LOCAL_FILES") == "true") +index_name = "ai_app_index" + +print(f"Creating search index at {search_endpoint} with index name {index_name}") +print(f"Using OpenAI endpoint: {openai_endpoint}") +print(f"Using embedding model: {embedding_model_name} with API version: {embedding_model_api_version}") + +# Function: Get Embeddings +def get_embeddings(text: str, openai_endpoint: str, embedding_model_api_version: str): + credential = DefaultAzureCredential() + token_provider = get_bearer_token_provider(credential, + "https://cognitiveservices.azure.com/.default") + client = AzureOpenAI( + api_version=embedding_model_api_version, + azure_endpoint=openai_endpoint, + azure_ad_token_provider=token_provider + ) + + embedding = client.embeddings.create(input=text, model=embedding_model_name).data[0].embedding + return embedding + +# Function: Clean Spaces with Regex - +def clean_spaces_with_regex(text): + # Use a regular expression to replace multiple spaces with a single space + cleaned_text = re.sub(r'\s+', ' ', text) + # Use a regular expression to replace consecutive dots with a single dot + cleaned_text = re.sub(r'\.{2,}', '.', cleaned_text) + return cleaned_text + + +def chunk_data(text): + tokens_per_chunk = 256 # 1024 # 500 + text = clean_spaces_with_regex(text) + + sentences = text.split('. ') # Split text into sentences + chunks = [] + current_chunk = '' + current_chunk_token_count = 0 + + # Iterate through each sentence + for sentence in sentences: + # Split sentence into tokens + tokens = sentence.split() + + # Check if adding the current sentence exceeds tokens_per_chunk + if current_chunk_token_count + len(tokens) <= tokens_per_chunk: + # Add the sentence to the current chunk + if current_chunk: + current_chunk += '. ' + sentence + else: + current_chunk += sentence + current_chunk_token_count += len(tokens) + else: + # Add current chunk to chunks list and start a new chunk + chunks.append(current_chunk) + current_chunk = sentence + current_chunk_token_count = len(tokens) + + # Add the last chunk + if current_chunk: + chunks.append(current_chunk) + + return chunks + +search_credential = DefaultAzureCredential() + +search_client = SearchClient(search_endpoint, index_name, search_credential) +index_client = SearchIndexClient(endpoint=search_endpoint, credential=search_credential) + + +def prepare_search_doc(content, document_id, filename): + chunks = chunk_data(content) + results = [] + chunk_num = 0 + for chunk in chunks: + chunk_num += 1 + chunk_id = document_id + '_' + str(chunk_num).zfill(2) + + try: + v_contentVector = get_embeddings(str(chunk), openai_endpoint, "2023-05-15") + except Exception as e: + print(f"Error occurred: {e}. Retrying after 30 seconds...") + time.sleep(30) + try: + v_contentVector = get_embeddings(str(chunk), openai_endpoint, "1") + except Exception as e: + print(f"Retry failed: {e}. Setting v_contentVector to an empty list.") + v_contentVector = [] + + result = { + "id": chunk_id, + "chunk_id": chunk_id, + "content": chunk, + # "sourceurl": path.name.split('/')[-1], + "sourceurl": filename, + "contentVector": v_contentVector + } + results.append(result) + return results + +def extract_text_from_pdf(reader, file_name): + document_id = file_name.split('_')[1].replace('.pdf', '') if '_' in file_name else file_name.replace('.pdf', '') + text = '' + for page in reader.pages: + text += page.extract_text() + return prepare_search_doc(text, document_id, file_name) + +def load_pdfs_from_local(path): + local_docs = [] + print("Loading files from local folder...") + pdf_files = [f for f in os.listdir(path) if f.endswith(".pdf")] + + for file_name in pdf_files: + file_path = os.path.join(path, file_name) + print(f" Processing: {file_name}") + with open(file_path, "rb") as f: + pdf_reader = PdfReader(f) + result = extract_text_from_pdf(pdf_reader, file_name) + local_docs.extend(result) + return local_docs + + +def load_pdfs_from_github(): + github_docs = [] + + # === CONFIG === + owner = "microsoft" + repo = "Deploy-Your-AI-Application-In-Production" + path = "data" + branch = "data-ingestionscript" + headers = { + "Cache-Control": "no-cache", + "User-Agent": "Mozilla/5.0" + } + + print("Downloading files from GitHub...") + api_url = f"https://api.github.com/repos/{owner}/{repo}/contents/{path}?ref={branch}" + response = requests.get(api_url, headers=headers) + response.raise_for_status() + + files = response.json() + pdf_files = [f for f in files if f["name"].endswith(".pdf")] + + for file in pdf_files: + raw_url = file["download_url"] + file_name = file["name"] + print(f" Processing: {file_name}") + print(f" Downloading from: {raw_url}") + pdf_resp = requests.get(raw_url, headers=headers) + pdf_resp.raise_for_status() + pdf_reader = PdfReader(BytesIO(pdf_resp.content)) + result = extract_text_from_pdf(pdf_reader, file_name) + github_docs.extend(result) + return github_docs + +# === MAIN === +local_data_path = "../data" + +docs = load_pdfs_from_local(local_data_path) if use_local_files else load_pdfs_from_github() + +if docs: + results = search_client.upload_documents(documents=docs) + +print("Finished processing file(s).") diff --git a/scripts/index_scripts/requirements.txt b/scripts/index_scripts/requirements.txt new file mode 100644 index 0000000..7fbbe70 --- /dev/null +++ b/scripts/index_scripts/requirements.txt @@ -0,0 +1,10 @@ +openai +pypdf +# pyodbc +tiktoken +msal[broker]==1.31.1 +azure-identity +azure-ai-textanalytics +azure-search-documents==11.6.0b3 +azure-keyvault-secrets +datetime diff --git a/scripts/install_python.ps1 b/scripts/install_python.ps1 new file mode 100644 index 0000000..3d22a92 --- /dev/null +++ b/scripts/install_python.ps1 @@ -0,0 +1,22 @@ +$url = 'https://www.python.org/ftp/python/3.12.3/python-3.12.3-amd64.exe' +$output = "$env:TEMP\\python-installer.exe" +Invoke-WebRequest -Uri $url -OutFile $output; +Start-Process -FilePath $output -ArgumentList '/quiet InstallAllUsers=1 PrependPath=1' -Wait; + +$baseUrl = "https://raw.githubusercontent.com/microsoft/Deploy-Your-AI-Application-In-Production/main/scripts/" + +# Script list +$scripts = @("process_sample_data.ps1") +$outputPath = "C:\DataIngestionScripts" + +# Ensure the output directory exists +if (!(Test-Path -Path $outputPath)) { + New-Item -ItemType Directory -Path $outputPath | Out-Null +} + +# Download all +foreach ($script in $scripts) { + $destination = Join-Path $outputPath $script + Write-Host "Downloading the file $script to $destination" + Invoke-WebRequest "$baseUrl/$script" -OutFile $destination +} \ No newline at end of file diff --git a/scripts/loadenv.ps1 b/scripts/loadenv.ps1 new file mode 100644 index 0000000..673701f --- /dev/null +++ b/scripts/loadenv.ps1 @@ -0,0 +1,64 @@ +function Ensure-AzLogin { + try { + $accountInfo = az account show --only-show-errors | ConvertFrom-Json + Write-Host "Already logged in as: $($accountInfo.user.name)" + } catch { + Write-Host "No active Azure session found. Logging in..." + az login --only-show-errors | Out-Null + + if ($LASTEXITCODE -ne 0) { + Write-Error "Azure login failed." + exit 1 + } + + # Set Azure subscription + az account set --subscription "$AZURE_SUBSCRIPTION_ID" + if ($LASTEXITCODE -ne 0) { + Write-Error "Failed to set Azure subscription." + exit 1 + } + } +} + +# Check if user has logged in or not. +Ensure-AzLogin + +# Only load env from azd if azd command and azd environment exist +if (-not (Get-Command azd -ErrorAction SilentlyContinue)) { + Write-Host "azd command not found, skipping .env file load" +} else { + $output = azd env list + if (!($output -like "*true*")) { + Write-Output "No azd environments found, skipping .env file load" + } else { + Write-Host "Loading azd .env file from current environment" + $output = azd env get-values + foreach ($line in $output) { + if (!$line.Contains('=')) { + continue + } + + $name, $value = $line.Split("=") + $value = $value -replace '^\"|\"$' + [Environment]::SetEnvironmentVariable($name, $value) + } + } +} + +$pythonCmd = Get-Command python -ErrorAction SilentlyContinue +if (-not $pythonCmd) { + # fallback to python3 if python not found + $pythonCmd = Get-Command python3 -ErrorAction SilentlyContinue +} + +Write-Host 'Creating Python virtual environment ".venv" in root' +Start-Process -FilePath ($pythonCmd).Source -ArgumentList "-m venv ./.venv" -Wait -NoNewWindow + +$venvPythonPath = "./.venv/scripts/python.exe" +if (Test-Path -Path "/usr") { + # fallback to Linux venv path + $venvPythonPath = "./.venv/bin/python" +} + +Write-Host 'Installing dependencies from "requirements.txt" into virtual environment' +Start-Process -FilePath $venvPythonPath -ArgumentList "-m pip install -r ./requirements-dev.txt" -Wait -NoNewWindow diff --git a/scripts/loadenv.sh b/scripts/loadenv.sh new file mode 100755 index 0000000..f4e2c37 --- /dev/null +++ b/scripts/loadenv.sh @@ -0,0 +1,34 @@ +#!/bin/sh + +echo "Checking Azure login status..." +az account show --only-show-errors > /dev/null 2>&1 +if [ $? -ne 0 ]; then + echo "🔐 No active Azure session found. Logging in..." + az login --only-show-errors +else + echo "✅ Already logged in to Azure." +fi + +# Only load env from azd if azd command and azd environment exist +if [ -z "$(which azd)" ]; then + echo "azd command not found, skipping .env file load" +else + if [ -z "$(azd env list | grep -w true | awk '{print $1}')" ]; then + echo "No azd environments found, skipping .env file load" + else + echo "Loading azd .env file from current environment" + while IFS='=' read -r key value; do + value=$(echo "$value" | sed 's/^"//' | sed 's/"$//') + export "$key=$value" + done < " + exit 1 +fi + +# --- Resolve script and working directories --- +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +LOG_DIR="$SCRIPT_DIR/logs" +SCRIPT_ROOT="$SCRIPT_DIR/index_scripts" +PYTHON_EXTRACT_PATH="$SCRIPT_DIR/../.venv/bin" +PYTHON_EXE="$PYTHON_EXTRACT_PATH/python" + +# --- Create logs directory if not exists --- +mkdir -p "$LOG_DIR" +LOG_FILE="$LOG_DIR/process_sample_data.log" + +# --- Start Logging --- +echo -e "\n===================== Starting Script =====================" | tee -a "$LOG_FILE" + +# --- Python Executable Check --- +echo "✅ Python expected at: $PYTHON_EXE" | tee -a "$LOG_FILE" + +# --- Define script and requirements paths --- +REQUIREMENTS_PATH="$SCRIPT_ROOT/requirements.txt" +CREATE_INDEX_SCRIPT="$SCRIPT_ROOT/01_create_search_index.py" +PROCESS_DATA_SCRIPT="$SCRIPT_ROOT/02_process_data.py" + +echo "Using Python command: $PYTHON_EXE" | tee -a "$LOG_FILE" +echo "$REQUIREMENTS_PATH" | tee -a "$LOG_FILE" +echo "$CREATE_INDEX_SCRIPT" | tee -a "$LOG_FILE" +echo "$PROCESS_DATA_SCRIPT" | tee -a "$LOG_FILE" + +# --- Export environment variables --- +export SEARCH_ENDPOINT="$SearchEndpoint" +export OPEN_AI_ENDPOINT_URL="$OpenAiEndpoint" +export EMBEDDING_MODEL_NAME="$EmbeddingModelName" +export EMBEDDING_MODEL_API_VERSION="$EmbeddingModelApiVersion" +export USE_LOCAL_FILES="true" + +# --- Install Requirements --- +echo "Installing dependencies..." | tee -a "$LOG_FILE" +"$PYTHON_EXE" -m pip install -r "$REQUIREMENTS_PATH" 2>&1 | tee -a "$LOG_FILE" + +# --- Run create_search_index.py --- +echo "Running $CREATE_INDEX_SCRIPT" | tee -a "$LOG_FILE" +"$PYTHON_EXE" "$CREATE_INDEX_SCRIPT" 2>&1 | tee -a "$LOG_FILE" + +# --- Run process_data.py --- +echo "Running $PROCESS_DATA_SCRIPT" | tee -a "$LOG_FILE" +"$PYTHON_EXE" "$PROCESS_DATA_SCRIPT" 2>&1 | tee -a "$LOG_FILE" + +echo "✅ All tasks completed successfully." | tee -a "$LOG_FILE" diff --git a/scripts/set_conns_env_vars.ps1 b/scripts/set_conns_env_vars.ps1 index d3aacd8..437d047 100644 --- a/scripts/set_conns_env_vars.ps1 +++ b/scripts/set_conns_env_vars.ps1 @@ -9,12 +9,13 @@ param ( [string]$resourceGroup, [Parameter(Mandatory=$false)] - [string]$workspace, + [string]$foundryProject, [Parameter(Mandatory=$false)] [switch]$includeVerboseResponseOutputs ) +# Use environment variables as fallback, updated for Foundry Project if (-not $tenant -and $env:AZURE_ORIGINAL_TENANT_ID) { $tenant = $env:AZURE_ORIGINAL_TENANT_ID if ($includeVerboseResponseOutputs) { @@ -36,15 +37,15 @@ if (-not $resourceGroup -and $env:AZURE_ORIGINAL_RESOURCE_GROUP) { } } -if (-not $workspace -and $env:AZURE_ORIGINAL_WORKSPACE_NAME) { - $workspace = $env:AZURE_ORIGINAL_WORKSPACE_NAME +if (-not $foundryProject -and $env:AZURE_FOUNDRY_PROJECT_NAME) { + $foundryProject = $env:AZURE_FOUNDRY_PROJECT_NAME if ($includeVerboseResponseOutputs) { - Write-Output "Workspace (Project) parameter not provided. Using environment variable AZURE_ORIGINAL_WORKSPACE_NAME: $workspace" + Write-Output "FoundryProject parameter not provided. Using environment variable AZURE_FOUNDRY_PROJECT_NAME: $foundryProject" } } -if (-not $tenant -or -not $subscription -or -not $resourceGroup -or -not $workspace) { - $response = Read-Host "Start with existing Project connections? [NOTE: This action cannot be undone after executing. To revert, create a new AZD environment and run the process again.] (yes/no)" +if (-not $tenant -or -not $subscription -or -not $resourceGroup -or -not $foundryProject) { + $response = Read-Host "Start with existing Foundry Project connections? [NOTE: This action cannot be undone after executing. To revert, create a new AZD environment and run the process again.] (yes/no)" if ($response -eq "yes") { if (-not $tenant) { $tenant = Read-Host "Enter Tenant ID" @@ -58,23 +59,23 @@ if (-not $tenant -or -not $subscription -or -not $resourceGroup -or -not $worksp $resourceGroup = Read-Host "Enter Resource Group" } - if (-not $workspace) { - $workspace = Read-Host "Enter Workspace / Project Name" + if (-not $foundryProject) { + $foundryProject = Read-Host "Enter Foundry Project Name" } } elseif ($response -eq "no") { - Write-Output "Not starting with existing Project. Exiting script." + Write-Output "Not starting with existing Foundry Project. Exiting script." return } else { Write-Output "Invalid response. Exiting script." return } } else { - Write-Output "All parameters provided. Starting with existing Project ${workspace}." + Write-Output "All parameters provided. Starting with existing Foundry Project ${foundryProject}." } -if (-not $tenant -or -not $subscription -or -not $resourceGroup -or -not $workspace) { - throw "Unable to start with existing Project: One or more required parameters are missing." +if (-not $tenant -or -not $subscription -or -not $resourceGroup -or -not $foundryProject) { + throw "Unable to start with existing Foundry Project: One or more required parameters are missing." } if (-not (Get-AzContext)) { @@ -85,7 +86,8 @@ if (-not (Get-AzContext)) { Set-AzContext -Subscription $subscription $token = (Get-AzAccessToken).token -$url = "https://management.azure.com/subscriptions/$subscription/resourceGroups/$resourceGroup/providers/Microsoft.MachineLearningServices/workspaces/$workspace/connections?api-version=2024-10-01" +# Updated API endpoint for Foundry Project connections +$url = "https://management.azure.com/subscriptions/$subscription/resourceGroups/$resourceGroup/providers/Microsoft.MachineLearningServices/workspaces/$foundryProject/connections?api-version=2024-10-01" $headers = @{ 'Authorization' = "Bearer $token" 'Content-Type' = "application/json" @@ -95,12 +97,12 @@ $headers = @{ $response = Invoke-RestMethod -Method GET -ContentType 'application/json' -Uri $url -Headers $headers $connections = $response.value -Write-Output "Connections in workspace ${workspace}" +Write-Output "Connections in Foundry Project ${foundryProject}" Write-Output "----------------------------------" Write-Output "Connection count: $($connections.Count)" if ($connections.Count -eq 0) { - Write-Output "No connections found in the workspace." + Write-Output "No connections found in the Foundry Project." return } @@ -191,3 +193,7 @@ foreach ($connection in $connections) { Write-Output "-------------------------" } Write-Output "----------------------------------" + +# Set Foundry Project environment variable for downstream processes +azd env set 'AZURE_FOUNDRY_PROJECT_NAME' $foundryProject +Write-Output "Environment variable AZURE_FOUNDRY_PROJECT_NAME set to $foundryProject" \ No newline at end of file diff --git a/scripts/set_conns_env_vars.sh b/scripts/set_conns_env_vars.sh old mode 100644 new mode 100755 index c978ecb..829bf45 --- a/scripts/set_conns_env_vars.sh +++ b/scripts/set_conns_env_vars.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Usage: ./set_conns_env_vars.sh [--tenant TENANT] [--subscription SUBSCRIPTION] [--resource-group RESOURCE_GROUP] [--workspace WORKSPACE] [--include-verbose] +# Usage: ./set_conns_env_vars.sh [--tenant TENANT] [--subscription SUBSCRIPTION] [--resource-group RESOURCE_GROUP] [--foundry-project FOUNDRY_PROJECT] [--include-verbose] while [[ $# -gt 0 ]]; do case "$1" in @@ -16,8 +16,8 @@ while [[ $# -gt 0 ]]; do RESOURCE_GROUP="$2" shift 2 ;; - --workspace) - WORKSPACE="$2" + --foundry-project) + FOUNDRY_PROJECT="$2" shift 2 ;; --include-verbose) @@ -31,28 +31,29 @@ while [[ $# -gt 0 ]]; do esac done +# Use environment variables as fallback, updated for Foundry Project TENANT="${TENANT:-$AZURE_ORIGINAL_TENANT_ID}" SUBSCRIPTION="${SUBSCRIPTION:-$AZURE_ORIGINAL_SUBSCRIPTION_ID}" RESOURCE_GROUP="${RESOURCE_GROUP:-$AZURE_ORIGINAL_RESOURCE_GROUP}" -WORKSPACE="${WORKSPACE:-$AZURE_ORIGINAL_WORKSPACE_NAME}" +FOUNDRY_PROJECT="${FOUNDRY_PROJECT:-$AZURE_FOUNDRY_PROJECT_NAME}" -if [[ -z "$TENANT" || -z "$SUBSCRIPTION" || -z "$RESOURCE_GROUP" || -z "$WORKSPACE" ]]; then - read -p "Start with existing Project connections? [NOTE: This action cannot be undone after executing. To revert, create a new AZD environment and run the process again.] (yes/no) " response +if [[ -z "$TENANT" || -z "$SUBSCRIPTION" || -z "$RESOURCE_GROUP" || -z "$FOUNDRY_PROJECT" ]]; then + read -p "Start with existing Foundry Project connections? [NOTE: This action cannot be undone after executing. To revert, create a new AZD environment and run the process again.] (yes/no) " response if [[ "$response" == "yes" ]]; then [[ -z "$TENANT" ]] && read -p "Enter Tenant ID: " TENANT [[ -z "$SUBSCRIPTION" ]] && read -p "Enter Subscription ID: " SUBSCRIPTION [[ -z "$RESOURCE_GROUP" ]] && read -p "Enter Resource Group: " RESOURCE_GROUP - [[ -z "$WORKSPACE" ]] && read -p "Enter Workspace / Project Name: " WORKSPACE + [[ -z "$FOUNDRY_PROJECT" ]] && read -p "Enter Foundry Project Name: " FOUNDRY_PROJECT else - echo "Not starting with existing Project. Exiting script." + echo "Not starting with existing Foundry Project. Exiting script." exit 0 fi else - echo "All parameters provided. Starting with existing Project ${WORKSPACE}." + echo "All parameters provided. Starting with existing Foundry Project ${FOUNDRY_PROJECT}." fi -if [[ -z "$TENANT" || -z "$SUBSCRIPTION" || -z "$RESOURCE_GROUP" || -z "$WORKSPACE" ]]; then - echo "Unable to start with existing Project: One or more required parameters are missing." +if [[ -z "$TENANT" || -z "$SUBSCRIPTION" || -z "$RESOURCE_GROUP" || -z "$FOUNDRY_PROJECT" ]]; then + echo "Unable to start with existing Foundry Project: One or more required parameters are missing." exit 1 fi @@ -64,16 +65,17 @@ if [[ -z "$TOKEN" ]]; then exit 1 fi -CONNECTIONS_URL="https://management.azure.com/subscriptions/$SUBSCRIPTION/resourceGroups/$RESOURCE_GROUP/providers/Microsoft.MachineLearningServices/workspaces/$WORKSPACE/connections?api-version=2024-10-01" +# Updated API endpoint for Foundry Project connections +CONNECTIONS_URL="https://management.azure.com/subscriptions/$SUBSCRIPTION/resourceGroups/$RESOURCE_GROUP/providers/Microsoft.MachineLearningServices/workspaces/$FOUNDRY_PROJECT/connections?api-version=2024-10-01" CONNECTIONS_RESPONSE=$(curl -s -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" "$CONNECTIONS_URL") CONNECTIONS=$(echo "$CONNECTIONS_RESPONSE" | jq '.value') -echo "Connections in workspace ${WORKSPACE}" +echo "Connections in Foundry Project ${FOUNDRY_PROJECT}" echo "----------------------------------" CONNECTION_COUNT=$(echo "$CONNECTIONS" | jq 'length') echo "Connection count: $CONNECTION_COUNT" if [[ "$CONNECTION_COUNT" -eq 0 ]]; then - echo "No connections found in the workspace." + echo "No connections found in the Foundry Project." exit 0 fi @@ -168,4 +170,8 @@ for i in $(seq 0 $(($CONNECTION_COUNT - 1))); do fi echo "-------------------------" done -echo "----------------------------------" \ No newline at end of file +echo "----------------------------------" + +# Set Foundry Project environment variable for downstream processes +azd env set 'AZURE_FOUNDRY_PROJECT_NAME' "$FOUNDRY_PROJECT" +echo "Environment variable AZURE_FOUNDRY_PROJECT_NAME set to $FOUNDRY_PROJECT" \ No newline at end of file From ff2e1663ce1f71ecb2e0ce041d999096d10c875f Mon Sep 17 00:00:00 2001 From: Prajwal-Microsoft Date: Fri, 11 Jul 2025 13:34:24 +0000 Subject: [PATCH 39/44] fix: Modified the branch name in scripts --- scripts/index_scripts/02_process_data.py | 2 +- scripts/process_sample_data.ps1 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/index_scripts/02_process_data.py b/scripts/index_scripts/02_process_data.py index 0133cba..9763fa5 100644 --- a/scripts/index_scripts/02_process_data.py +++ b/scripts/index_scripts/02_process_data.py @@ -142,7 +142,7 @@ def load_pdfs_from_github(): owner = "microsoft" repo = "Deploy-Your-AI-Application-In-Production" path = "data" - branch = "data-ingestionscript" + branch = "main" headers = { "Cache-Control": "no-cache", "User-Agent": "Mozilla/5.0" diff --git a/scripts/process_sample_data.ps1 b/scripts/process_sample_data.ps1 index 33d438d..c27aca8 100644 --- a/scripts/process_sample_data.ps1 +++ b/scripts/process_sample_data.ps1 @@ -29,7 +29,7 @@ Write-Host "`n===================== Starting Script =====================" if (-not $UseLocalFiles) { # GitHub repo base path - $baseUrl = "https://raw.githubusercontent.com/microsoft/Deploy-Your-AI-Application-In-Production/data-ingestionscript/scripts/index_scripts" + $baseUrl = "https://raw.githubusercontent.com/microsoft/Deploy-Your-AI-Application-In-Production/main/scripts/index_scripts" # Script list $scripts = @("01_create_search_index.py", "02_process_data.py", "requirements.txt") From 15248e49ee9826881f54e8bed80de68d0cb21660 Mon Sep 17 00:00:00 2001 From: Rohini-Microsoft <168007985+Rohini-Microsoft@users.noreply.github.com> Date: Tue, 15 Jul 2025 15:35:37 +0530 Subject: [PATCH 40/44] Add files via upload --- .github/dependabot.yml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..2c96c5f --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,22 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates +version: 2 + +updates: + # Grouped GitHub Actions updates + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "monthly" + commit-message: + prefix: "build" + target-branch: "dependabotchanges" + open-pull-requests-limit: 10 + groups: + all-actions: + patterns: + - "*" + + \ No newline at end of file From 7d787e2f3c60bbdac1cbbf30c15abf7de98f6699 Mon Sep 17 00:00:00 2001 From: Rohini-Microsoft <168007985+Rohini-Microsoft@users.noreply.github.com> Date: Tue, 15 Jul 2025 15:48:12 +0530 Subject: [PATCH 41/44] Update dependabot.yml --- .github/dependabot.yml | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 2c96c5f..c2fbfc4 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -19,4 +19,19 @@ updates: patterns: - "*" - \ No newline at end of file + # Grouped Python dependencies + - package-ecosystem: "pip" + directory: "/" + schedule: + interval: "monthly" + commit-message: + prefix: "build" + target-branch: "dependabotchanges" + open-pull-requests-limit: 10 + groups: + langchain: + patterns: + - "langchain*" + all-backend-deps: + patterns: + - "*" From a13b50e95709c0c3d6b0e156884b7c49a2b2a657 Mon Sep 17 00:00:00 2001 From: Prajwal-Microsoft Date: Wed, 16 Jul 2025 15:41:11 +0530 Subject: [PATCH 42/44] feat: EXP Changes to reuse existing log analytics workspace (#66) * feat: Implemented EXP changes * docs: Added Step to use log analytics workspace * docs: Added the docs for log analytics --- docs/images/re_use_log/logAnalytics.png | Bin 0 -> 145324 bytes docs/images/re_use_log/logAnalyticsJson.png | Bin 0 -> 201052 bytes docs/images/re_use_log/logAnalyticsList.png | Bin 0 -> 90986 bytes docs/local_environment_steps.md | 5 +- docs/re-use-log-analytics.md | 31 ++++ infra/main.bicep | 42 +++-- infra/main.json | 165 ++++++++++---------- infra/main.parameters.json | 3 + infra/modules/virtualMachine.bicep | 7 +- 9 files changed, 158 insertions(+), 95 deletions(-) create mode 100644 docs/images/re_use_log/logAnalytics.png create mode 100644 docs/images/re_use_log/logAnalyticsJson.png create mode 100644 docs/images/re_use_log/logAnalyticsList.png create mode 100644 docs/re-use-log-analytics.md diff --git a/docs/images/re_use_log/logAnalytics.png b/docs/images/re_use_log/logAnalytics.png new file mode 100644 index 0000000000000000000000000000000000000000..95402f8d133a44749ea4d98659bfb45f708d59ae GIT binary patch literal 145324 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�zi|Nx9mKNGlGsCs(S!fXpzLs6WS?os$#ZH6u*~=o<(FoffaP3@@U@F zdV^G1wgB5}Iix53LdIH|wni2fj}{iC znZD4$CPCo=K(D46&ntfNS=jaw5lbG2rmO$$CGRsdD2Tw0WgVOOpJ}!ek=F_aX_RS%vL#O@i~6 z&IdUc+r^X2e|NEVTm~dCh#S)G;u}?NVJ4@EB!nT{{=?K>)b%xmzLU&g#$;?n;x&5aW2531a^57f4z0ujxF{w<0v1k9B z`e3-R@&R#oUir8t<-<)dxUy0l%D_nQo-6M$eu-IAJ5fo#NU2J_jGUVzmM`m0*i9wn z`Uw$^qNilnxv-Ebr_otyfm@6?Pom2x%dlH(X!Fu=GHz_QHRbnB>PQ~Anex@8mrhQ# ztw(fSlWrC(9GEhq#t4>xJLsPz!fHWHLa>c)Sr_~$_PLPU-t7B!V>@PfX_v*U>x2Yh zOQ=8iri(8rSg`xhXuCtr7wa}>pIAjA9($-jZb179cYO~ z$Upw=8mZl4K(4(wAJ&2Pzp1wSOs$)R$)AHT(*`4$JQzarq4 zc;io{R6cbA-Zd~5PKx8f&0Aq8Y1mw=N3ZN+i0)R+r@6Heex_kMq(}A2`Ib^c+s;v6 zTrhbdU%Nv)!&;<#_!pBu7$??;Pi$vOFKETvqbemis>futpYL)Mn|sfe98neo zfOBuxnP(y8Qy+9bk3}eYVE>FCz1h}${h)SPfI9<1CJx0ao&`M?68d2YTqG! zUIGxq0>A+!BV)yO4SaSQz2QNsY02?c7ktRPhKB*5#oGJU8g?D}2ghV7WdA9|S3zc{ zVM7p9)m^m^*%(Q^E109-X|g7uPo@siFi>;#&7ZG!93XF?P>Cm<%tLO+FXXV}YzUbl z#QFX-8$}WOen_|ruV{-EiqeFUJ*X3yJaayz93J934${PNlePTj_?@@gesd%4+5p!d z1mDq9zI<1%<@~EKB-<*v9Y&HeTY@5Qf>J~ml+Ae7i&ldb+2HzZy9!et4$)H`)GduQ zhX-D?02WyH)6Ysm9Z2#;@U!VJ0&rj2{!5$3HPDftI@!_M!s;5QL(&OxS$Umn?kCe@ z_1aVq&qSrOCh83EL!uonbS-iFkEz<<^=3QYp-=6=dJaJK2F90@Y`rkH9mtAtU8y|ah{Z= zeexC=;7su_kvvS0>5iM!j^`egxG(7TMfmg$5L!Ni1QV3wHP(rOCv19T*5Cv>~b zMtOEUi>yokiadY8O){+9YTO8baJ1YkKP_Yh?w7YZGU0917>Ijw%@U)NB{C>EM-I?H z1s@1Zsh753$KzC32&@A=uB4rJb({+^HkOk_>z#_UF!D9ZzEWB!X3lx}HAhi6@107Y zF>vO>;jypXTU)>eczUn7xbttm^OK%34K0UsMdU1pHn{K)ADpNZFy-FGz)|t0)nXkz z;>K&3%Enr9#aIt*RUNIvU%{oIa9dB7_ndl1aqDV%y&+mw3Y4?dqHAeV+P2=0=hP)@snkPZeLS%kbjP6`Fqhbp+YX3{YT^_4ttOn$4^YsoErWXJ~{&L>u z1pu&rKLN#4)6bIS4!Tv~T$Vx8Sa?72c%G(P@*8jw95+z9!1FGupVxzU@;5K10iTft zGGPALDgU4VXPWM3`?nXeyTMmaMQpECug67fQH1SE6y7`pxum^`9^b{W2NN@&&6Tg` zy(}1dqRe(+%&i1CT1bE-B{=)NYSvaVUBcz~fFP zo}hYB8}8;q!@v|hwxbT2pkb4`>eZchV+ItSr=#!uouCi-vvt^L^?bv<6Wza*-cdcO z8W0bBL!rhMErSxJ@`vJ$eD&#Qwe%G7H9%) z8b?3*tpjXW^;y3X^w7o5pK7wNiuHs3zbWgS_bECXeyeB$iag;^&TE&Au^Ij4#G#%i z)5R0=r`0bNd_>(6El{|kY@2MD*4M-&y|?2Dkt7 z!yWYPMuGMf-HVt0`=_tE{yiV%fk>stkEIWYTfgUnWM>g#moKxP4s+}3?MGi4FVAJt z^}G1^qkr#iME74b7cT3fK#q9sC94>#Tz4WD@-}zh9;26_Z?lo3;J@D+WkI=vOFnNH zeoRTG>$lKlu`JRV@3JYzb-2@OiX3He!8P=;rhFNpQ){w%z3){9RXdQA|7+ zAfGxaGQProa3c{Ls+els(2k4B3#SaSw<>W;d~lvrDw5yWM>E&LpVa zkoxoouGZAN-b7-(x9Ch{SQ3}SuCdjXGiUj-f%Wo(3eZkeIWj7=v^cmRIgp-E@U;*G zIi}ag0!df2&wZO7@srpf+)BcZ-_Z8?BX;rpYMuk%p5y29&p9%b3vpTb6|diPsT<$T zP;Lg-_;!kE$}{D4WDHaGU%sq#Qp%dFTjv>c>)fFv%zeuKdJbPJxOe;Z!TaYz4;@yQ z%!04W(M0JCRk&gi6-uAq7({7j+H}rL6|5TN*D=-yj_s7Zg&yCMconL~O z9RF>7>ichgM}7UeaLS1^efm%_O<&3EIU@iW@IvkXPw?2Kv`(k99)LSjs`9D}0lQgYw{ zL^c`6#luRIfrmk)qjjHZZ6n?0gw}6vvp-uh7CD+lK`-IkxEM0^3Cr;9-N%k)P(1;0 z)y+-$<{jP|=rc+HyR^Se5u9a?hkxW*<(vveT)!== zF$e|ZA)M9G#wN3}m?~IaMPR!fQ!UdAXVqCRWJ&UFkw$um$eIXU3(7n@VO&#CzyZ;?G?xR+2-R$q?$6l{get2$gp&aU~IG|^E@C({M8KZw`d9NUF ztqkjm6AF&1h;EeltS{dLhskU$*BV5JwSsdbX-_{=80t6!d)JyjLf1M&x5+7a>6Gu7 z{%m3D^d7TM;pyo0&vXZnLe&}U);Qh&y%`0_Con;~mTe$Hp3yrr3@ zo$1@H0=3c2c+V~Kf_8$ANOaC@ah|xCw$Fhi-bB0dvcCA}Z0@oD?PPy&G)KG-&H%&u z{YuT?75r6HkGJK!kJIAK$^H%ayuI~G1J!AhN7r)CcJdY3ZNgY`_6*=lguC{4h!41L zsLdLs7&gWg2n#-?<1hdg`r^mYhi|5@c1ATWPC3tFIaTB$asN8%h_C-T>W{#@2Hs0vE+PMqby-%fi_mt1#Em;Cr=MT&36ya6v(`vT1=eiAvjoNh=c{hzhp!dTBTra|vca0v^XZ1JSkn{Z%ll6R?WkXKB zvH^sJg~h(BD7j?;@zy++;j1)*8Fx8Mo2F$gr~0mdYdWkIvGeBWzr`d#wL)JGp$8h< z<-|+}LdgY4bAJ^nBA9Ud^`V{JbBBvyc0Cxrk>NC@sA}9WDdqd@WGYp`QO{H)>B#rU zL_RTQwkBTKhK35b&a-z+vA3=AwRgxr2BHg0$6IbYAAy76f61RT9l-9Vo+L-*EwrOt zUs#JsW=&k0-TurH^T0}yS)B5-AVUt z-vZXW(TKb9*VuzAl*e`xQe&#;pJty-W;)fdv7F@G?t0eEp; zZq$Q0`O~7!XTPbI{E;9>BQHna{ipMVYKzMkcC&ecR!B+{bce7(CV3X~+a`E=ORWL@ z>r^2xpDWC*?YEnKGxn-?-fcB^-a&| znbyA0jO8V5Z&fgo&2#gExO^u2vNB7-$$_BQPb5pFek7>7`ZW9L8qEjY$h&y)>SyH- z4Orb^ZgLGOCjEWW@z`$`;XHmj%iwq>DRBn zt~8+;t8({Pt$e5%9sxS7;tNZQTpjjPRiS6=cLg2$#oD(-?N|HqETAAxWsIa~y)`%2 z?j;o;0pksu-rHTnoKfbn85tY8SfZHPR}RhyQ$kpprs3fRR)AYET{T9S&2D1s6t$1& zo8AY~f7KeV99G^Nt@dI8SG{=JxT_N}4R1E$R+JjfT}_$T%>g-)wgZN8je;LGKT3wb zx%3fy^?$8+H49#2X672_IzWJ<^^uS)zQ_{Us-Hwoe^p%OKP_46XcI;gkx7T#$4rh- zPtOI*lFx9Bha6ior`(Pa&>31j5qSEEqVFwE&j@mKdM-IO=|^bH0_Qy4bxmBr0 zy~u*^(>Et}>OzvQdU5$TS9yBI><(OhJ+8%6Vuu|va^Pb~t{WMN(e*Xzz!Hq~Z9)%z z$*b@LuZ=y{6Lq&_N_i!z;bX{`n>47lF;Bv;8on_}4~W5YwLheCC@a>I7&Z+-L>9U& z;oBu%f)N8WV9S*wi_ou)ndrW_Ya>a|!gQ$KM|Hck`{>18GpSG%s;>!kZ@BHa<9qtT zk@$;?U*oo8S7f{|eE|kge^f2(s!>@xlHN0M+6Ne=NR_Dj z<39th<)s3je$2pna+O1^Ounj2ontti!%zKX#>)G$D(}H#2ezot)z4={@(MsgS+ruu z%{r(FX=Jd9+DekI-+}!IS2w8|5>fQ&E3J&U`5c{*eFaCq%xwuzS*qo>JC17peF6E= z^m4MYr@a2yeq$9aF|Pt{qzi)>J3dkqq}4=oYJYaI67?PN{`IGnlMdVpI5N~`8T-{+ z+~1b<_M9^pjzKoY?f=1y+{v=MY%(BrDso(7r8r%dM|H+4&B0g{VHF|=-{yHZCILQk z&zLB$_IGJRVNd3Jr{|POqZF5TW!S&aQp>%5AGo8?E;#hBlgsm?YX35Ontx^KRL?e4 zeq7K?RVt2puJl6VjB|6;Q4y73pL#ph;PQiqqF?kFo#kVDMl~&$F{yw4>R=|8UcAEg zc{|ue9_HB8nt$z?G%#NI_pjxpX-gky-_ZyGGl2Sa9QkQB9Y1dAUJ7&I&#ma|7Fn^O z@fA=7Mbd>EBWW)Ka(n*|b8i_|Roit9gQSEYEh(UM3lfrofKo~$oq~Y0NQcrb($dn6 zG}0i_At2Hr(jXlY-`rmJecjjnyzg;*&!6u&zV$=48}{C7uXUdDoO6tE&e53pEb3+| zym4vab2JYT57Qf1{XJjCHBa2^=wpaC>8Kmdlq-dQ=W(fdm}+D)D8GFhiBF@kaWMAn zYk;X5t1uQGaiRA3x(=RQQ$yE<+^*1h-uSZbb`$&Vu!LQ^geO`@l`8Z5u~(zPSy`u_ zV!th)s~PEiSa|x&*!|?8r9|ZLV;PFOYM~*hjYO;BVQziO*@@>zdB2v3Whk&)wy_qF zLK=%n3j0`Sd1y~!^pE*-EwXU#BYS7%U6&8t51x+m74TMz&mXd2kajKQ^*A9wM}o3K z>f2An2fi%fcS#V-!bmThL~Xp$j}LTVE5#0;5BjyH_?aBTgD*sWP~?^;!}~*g#k?|V z6H=C5Y`@Qdr7M}l@|kPoNphk(afe6F+UaN+vun}gRu8qr3-%DbqFwz#WqkdBfG0bc zm?6q8?>nfBEk3Otu)k-h`Sna|)I)E4zcSz#ujoU3TTa%U1J3o?n74ru`rN^qJH!5( zT4H+(P3%l<&$Q)*TWd=fd<*io=+%fg!-;09^1a=)M71G`r^CFhC}I?z5xT4Pi|)4M zFCImg!#`>MlLxJcT6YLz4yw6V`0o0G)gO#*aM$2L=V;Y%`+D~wjLrzk_8%4>Q6*-| z2{39Qga(F%G262RBR$p<5E^};eRQpRQOson9}=B(yXc>=`bdJrHZyd){S<4()VLWRzaZ!!k71b4;w?nJf1XN&HL`}4XE`x zhQDT7M~!rChh!)s@Y? zqD}PZiHPB_quock?!CPW7VZHR#FrIp$}uXV<{~9U(L*O8+M_+Z@I-+CgY^*2!p%~( zh;5u&6%}nJrySKkjqcYwjy`FV`{nkEpIdvT*cnmR^Le0#*Tjnt3Fo-hJ@TRC4wi}? ziYPe{GF!~8ugGH750vXvmoTTJ`Cpf< zzfU!Iv$S4%x8N3LZ|vdrBa4b64Hd4|S5VYeI99RlUh%sRH*<;krwbOW_X`U~Hz?L( zPep}(Py~K(WS+_?CfZ+x0artYpG_I0A3LB9;ciLz(}J7-ADCm8+dxiAY9ARYT$a$# zu$Vnn5?I?wJLXbkq!#bW2QM zTYK~4+n~C)+qp+e-SRXAYCq+ET-L3D7b0$P@rQK+W@^1?d%uF?z9~fMQWIrX5qs3N zyE#QI)}IEuc=as0e(JiICF!6X2|n@c*yABppSV6n+afVuXU87);cSju4~9ffH?|RwzY%@Ak6YeY{Jn%-3DWMno@5?7$No_x7i`Fr+Pv#TbicyFn$% z&q>gH9(CBglh&G0H-YO;|zICh5L@npm-K*7FZ`7UV6IdqM;! zG>O~>h*IL>5Lb1@`17CX;l=<7ZDm1R$OlRFmEi`BDAncyFMV{P#1IwYofY8@rkUYf zL;dwi-@ts{7sHg-6#F$Vm$$`RPKY`Sg9UF!VfL%h!q|s+7yKcEoikXxTA+^u>mWIQ z|5HhZY>&21THUPoCG%J5^;j;OGNX6J_aAy36z8rLv@$2lt$o#CwwNB)i+EeL)5!eu zH@ii+7TBGId@A%&_KYR=(s%?3<@W6-ADBFnIe!z=pNg6|S>+Qw^vSpV!vEMbQHfiI zB80A`_*)j{r@*|43&bFedYew^qcLU2^s|a6=PPjz8pn&-ZAt3_7W2nnA1!(01k+{*cb-yk>p6K;}5ij_{MQPHrLZC`=DwOb&|(9g)6CiS|AhI*fmS6 zCTg>N1xXXq@EB%yY}SZ@v-NUbK3^$1W_z2o!DTJAx*zaebmfMorcF<*Vn#InEL-ok z8_=w1I&$XSZF?;_`CAEY#)VWrl9E#00jPnh+D3=BJeG&A|cLtIX` z;tb`vPU8v+5vq-Pv8L~c>O5&i#}ibw<-QYXeEB-}A(`5~;bEw{yd~Ox*?c)SdOeH< zi58d31RJ96iR8F@O8^tQ@sdPRCY+sT;OF{d;*Amb^Y(|cbHgVNzE2{c4vN3yfCZ@= z6Hj6-uOjt5RiiX5VVZ*cMvc&1j^5rvELX2F8bDu(bqFn_(Z|l_b*=DAZ*`zyhNv7< zOG-SQS(LfPvK*?W^BW$JJiM%bc@Rv1@@C0SWX9x@X;~aGIme3i($93SKPJwxiW3WT=a-Wem9eg-J5mN&PBVS${s?qx97Ho1j>s zfbEmVM|6?qUMl-;hGZF=neU~$X&xxTX^Mm6+Wng@(5DCSJ@tgqgG(`%Wk&8)BT zRva)*K%%hu`r zednwSA`ee~;r#Kmiu*3^KjkFVO1}=b^h8ydVYl_N@Me zdjWgmA0}O$(hy?}HGJonA^i-yk0crF1!gc{Ctp&bA~~8b=1+BQC2I#KdsW?nYGiIj zmGm6_Oi4#gilX{(yuw(l>V;0h)HZ-#zuF?2-f1V!wzl-QIs^#`jdt{(ciJ9>r`peo zh)iT0Jv;At|D43n*B&AaARM&C=vZjuoXKJVkjcjh& z6WMhJ)3j~U#!&0GGz_MPv966-Y9fYcFw#7HAnK8d-Ouw+MD@-uo@}|Tf3AenX}TGi zzvf!y==3Hf#WSeI?cLXdSJCIJR}v7XV8a=~e{Cv7)J5JlnT?H2P8d&DM1)Q20t2!9 z=g4SRu>JgK1z%l^{jfD_Z~5IMMweobJq`YABP~b?&nO!g!_$l zfL=GzZO{*y{2*lA_809?;T!CQO0X=Ko)1EGfv0j>hyBxY3b3T&qD1BVxuC2dS`If4 zTqNIG>PrFltoA6+u;)(~p|(YJ2tItSrnE#^fO4;WN43`KK2zGD1!|zN{dUToag7l0U{@Zk}~bWdTn^*YW*?}G36rd ztoTOC9n~q-tl`7$NrBFXskMj~_zFHnHh`ZC*(}U@S8OR8@d0V0`}yE`LNSEP>ovF0 z_K7U~lyvYur(j=@Eyv7aEC`xyqO&(Am&(G-HQXE-@bNX~1;|j$O4MibqkpqzlOwa2 z+@5n5+XP=>kThRWjEBZsSghjw>Bd(@owCYjANRV{E9nM7iQrZ=GL4tHa2;RWKJDps zgUiKV#7ndL!|qkV^~=(1-~C?nC8Jr($&EHBAY!a))WaXU&i52YmuFFNrfn&_aBR@s z_R238AF3!^!BBBx6LQB(TzeirbKWQO9sl}J0R746_w7K@M-TzY_@8qzC?y?qM`b~R z@f`3;2KV+>2;VHJ5|GAq1^4@pyU>c=R(*gq?jPVcAHw;$b$XiW&a*!M zy1KfWphqS3dRox5+W1Rk%gSt%m~UDW3Dbo&b_NeD^oTmmYN3#pnV*D|3Pt7xS zD^7ncVMSHNV}?k0NWF@4Y-4D8Y=K9_+UO+Cy=O0ix3?EZLqrr)kpm^f5| zRa3+F39oyw0hAjEnfPE~qpmY5j9jd6yR+bi8j`d4Ow!ec zf-kA;h^TeCZP;8D+9+~JmY%?gQe}@s(7{O{&cV6w#(gl8j9ZrT9;GTMD2Pnt`K|qf z1KPNro#n(zE?)NruQPOv+0I?l9xm%sM**S6vj904u%&LD;IoX3it@2})(^x93A$C# zie$4lyT=AmUIcxr==FD;J9>SpU!apCOtxv5RagFlHsgu8N5GcZ zOohp+1+y$@xc^$~PE&_}SkmL_7blddn$ptf4D!bWYV7P~wnIjC2bPAwjEJKC;Uf^Y z2aYFNs{7tWi8Tw>J<&Z;tkLuuu>n(`d{fbydZe02HS}k$n`q+BrRdPVbKk zURvwa*ivr`#QvEMf6@-bKQsx>)TJ@YOBmLBKg9J?D8(xH+9S67kB6o)u_u-;RnIPr zTee*j#ZxFFm%3=(u(?OTpJwWl6Si)>qo0otvXJvuDD?5U2?je;L`e9AgkiN}XWr`U z+Rfj)t`~EJaapga3cC_rQ&Pf{KeiW!)?7Uj=5S!(1@??bvwATZUsBVTCGQYFvmm+o zLmn-?+6ecCS36fiad78x*8XBF9sr&b>tk|}We`Bwi z@Z5mz=qS*rf!At8_Av(1=JezuSR#N+b>$#f=Dcz%@rKtsu8myfC@!vXSwz?S^omw< z*-i?zSik8S9-fjabmo7o@fI{f@+rJmLrB@*goLnq>XxI1kg!W1tc{kL&)+}WU%VnQ zcnICQ$nKfplz;rlZFAv%R%db*2N!2&wEb#eL@3dDuLgm^o6(6qr>2%xdQlREj*f16US1Fh`_tC8uY}P2{dHZPYkI@a0__s& zjm=Fv=yU`9Zkc;%WTcw653LXa0KalU_C=A5drqw0xuGaOpzSedA#aNXYs;aa|C*D3{W~PH znT%nzWG`sKBWS<3do#Mr?j8$v!HewJnlC1gs|Nad2yJ%5P|;`FUSr@57AN1kMQ{~K zTt)_i>Yn49rc|2y_wT>6-kxa)>AZgZ82lLwO-bz36Gxn}&vlS20FeA3n_WuHG@9D7-N>HHGR# z5QIAIw(nhAE2x+%ii?EVNE4d-MHYuf^gh0Mtn96onTC7$;K^-{{dagkJlNlY{iP=M zg1fuBUR&4XJGavI3A`9gyWVZ??RxKa2eQTW*llfS!3|{oO3TV?F^mcemw?xdj26iS z6Xoa645gJH2`fAZw>Br~`57_zZDx>WZr0=*ym%2_Wj(n7wy}Y%vJz*|`C$sLE}~es zK96!$`#rQy`XuX$41<4VWhJlxh^xSSb@jmD;5Vu)>}6gfBcrrW+?K1s!1h1K5XNS* z%%DlIpw%AM&n%Cy)TP@zJOrd1I?ZhXctceRmJl&m;;2RH=tV`TMVI?&xw)UzhWq;q zRq*nmO}Y?*iG@^~beQdYE1HR!*#d}qU;Wx{I{p!@XNpzK@*~21Gl-P)t?lnacBJN$ zJ<-yN3h~$exE*~xx-Lh5WCH&_l_9V1_wdHMm(cdce|AM_{8BloH!7ac7>WDFR^@zl z4!&<8LHZtOHrvDxV$1a1eV|GI{ON%uojs{}rBb2*qXO)z+NbY16JbM=4A4VRO6q}ftNl#wO+v-9?)Bd@=d3j0@Ou90l`|U#Z?cuR8|Ddv~V04Y< zaKZv!7j!}_SciO;_aWwxlatHJ%S)MME2%N}izoR+@u@i-)JREK|mA{>%kt>=P~wtB26o^JdH=G*clVA!sr}1#u#5 zkx}s`GxJr%~t zqANy6($v<@wD@TDtHG5Rd?0i68iX2?1+k%@Ektp=msYdkUvm@EAss(QYJud`o4a8uboBKJYIQ0QjS63HiM$5neo_4iW z3v`p6bN*dbKGRo3#mmQ6YS-opZX0KnKikl})7$)8Mn{RiW-GQ7Cfq^1HKm9>s(vUH zij57Yd_326Xzq=QG*Y4u*7b+&vl!RrLduNo_P<<5{Mllshfidrm!Y8{?f)$v?fuw2 zJ2A%|P|!X;YBlW`>FJ?0a$DLpg*CUP(uq)m`Qa@B$IW44=&=G<`wCJTbS2fSe?i68 z%U){rU0FUw_$u_Re}L=ewz9sCg7iK)ncHQTNchz+q&tl#=n_6YxBF#Sqht~B?1M5h zIsXft{JDH7x|VPH9%0~{V6fCZTh{94cT)`(IzEU+k?-E29vyDVO?f>8TgaWsZB}l( z1&x}Q=j7Q6$-%IIfQJH#*I5}ky>$2+DLQ1N+=2q*!;NjO<=@*oKe~*2<9BdLF3ZUj z75n4)-0oIXRCXjW{(Z~g!D%k!`@5K4u`{{4c(!~J*pM#%h=}dOJ6a-)E65iU{z4v5 zEP{k2zpYpOn4S5{l0Dnm2_viqC=~qG9D8S#OYC)dgg%U!)j~SP$8&9r($-efu05 z2iMrnUlA4-o^^ws%+#XpfzXS61Uj(jJowK$LbM%nT}IY!p{y!7Aw*KXppza40M()b?PC( zp%x~imkxa+rThwgu3Ao8n*wauTZ)qSxh==!5kqQytXKkanNpV?5_eGp?^neCyn`7L ziBcSKO<%7XrplQL5bsiQ4wtv9WsNmv)4nfc2?_}cToJpe#litU!r_9dcZ zkra#nF&awfIFA);GmH^3CQ&BxHE;6Q2+7#L4%I7yyG?Rqk4e9&3jd|Vd!V#uItYLJ z??aaOL8-cuijK;T><@cE4DNs5)4zVky!{_#B&8w1-TyfDFQDxD$Nym{rTl;Q44h)E zj*jdR~={wd4{nzjq@*2H| zJN}uWhwG&W8~M2T;uf8DA+=%nD1EP;v!T;?lBLlv8ty;^j>PT%aZ#IAlF_XOFrM9H z)d(+IWH-=ysH>$ns)<6;`Sd2=9X!&@7_2c`S1RZ#WIpuihOe)0yV`%9D@~{~d8r3c zDmIdB=?H;FdSlpKq?>GaWpobuU%9!pKotmu8>z;SmyW6`5u!Y!;J5PD;gEU!82>4+ zjru~v(vl8&4EI%$7uKtTa(f3ipj6hC%^EHcXjshu^PjpASo zP_vfY)7RGlb{2*z4MNZy3MWX4?uD|H))uLglhcP=jEfKg7g&5C=h?Whi&lRE8l-r| z>UnHOA@U4}L<@3uH6w4KTenazJUw%vVN=?tKLwLcsl}r77nApk|Gcv+O+Vni7Af$S zI(@oL-TtF_C#lo?qK%L@We+v{4)vmh{N1}@m2dJ|dTOm5Ozo{2-}#!fAm832tyF
|7M@CbQXQR?6auh8 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%0AxWpxw-am! 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 lPu=Q4tYK~_G-}mbF|%>Zmc65-+!{;w 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�?s_J7|SCjE)Hb+m4|?W;n(y$ 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�kd(iHEGCQ_?qn?X{x1#{BRv8;R4`PaG5b*r+e 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�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 z zpZ{}3gFjkH!+O(Q!}?NrLlj_TQCbDyawZ504Yj*(TO5`L;h?P+n-L$Opuj`+eULJp z?9EPN^_Gvj11qD^L{Yl#BwgiFLSs)}j}X8M1_9HFL_B{bqzq2!+q>af^j?*5{*+S}BSjXYg_^{D zNtIt`a9%z6_me`W(0aM*S~`$n#B@Z{us`|!L+G6SzyfOqU^Xh3=sg7Tdc&pGAX?>; z)~tTeo(9)u7FdJmdx{ZFQr3atx{w68cQe3Z2@=H=;A&6W+1W8c6p+eB`4SUTO-sY3 zdS0HHe9F}}bJPtFmILBd=~B<72}^wm;4CU;P9;A#B#FfGg6*gSx(21nUk=;wK^$- zTXKo*|MJccOx_A8f<@{_4ixgwdt8M9Csb;jrbhy>st@I>awM{wZ+#q?o`$F*s7O9N zHeTSy_@JRtpb-kr9oQ%J0c8*spV=3Tf-KVS!LbM9d1pPiD}U$rAVzl;=Y_{1HLBJX zy<)7AdP`EjuTZIzrU+BW=MY8lTtDF|1(Co@eVp!Y22b=6O^*xweG^ntx_@rEKgZ^q zL!`7l_{cL>;Gp;^LYt_p)70Gcodmc`ePybkN@WjOeRDeI*zSq zl;q^TAk=`i*b0l;?y-DTXYhGu{Y4r8F}eyI+SlDdWZQMM^(v$548KmDZjwe&CAiv;XkU=JdB!5o4YZia7%9P) zJPVT8^WOT@%+spWdEJ7I$x&xA_E`5BlLW1i9|fI{bMXwVPXUp_@d zL}V0zs|Juvbob}#-2iMqPp$ef@I-_4Q54Ym)TZRYD?q~pfNwqm*pInmk+0^P$C>)@ zhO?jdl!Wq}O1?QMk9Itl9WRonegsmQrKGg&SYApT%bMp@BiNwj4LH zED5?mM)mnj4RO-tk;nj?&3ny-!%@nK-7inC=Se+RCW2-CLZ(%U%{fWgkcQk9k>DQ6)FL1AVibc8jaICUSnN*NpWnNOnja;Va;f*K&(gL$B?J_Ihzr1`YGkhdb0=O8iTzn7 zs=8A6JySiemr0>X4`2k{NS-o_eT;+-dy=zlJ0TH(p*YUnAcMr>gJN|yG?(H0dz#J% zxhK**Oe$bk(S~-FC!OVYa(AltY6PY3)r^mYO<6vtUWt4%=WXbS^tkjEF63(a_tSsQ z$Wr!!D9T1RQ4Kgh7Ajd;S+q(;UKsq>pTR^x2$T;NpBBKM;&Rx*1IuTP4uC6y!;;;2 zixd(arv?Mt3Bapf=-ePdOMWY<2tf;*h7qgAiMsK zS~;z6ReP@=(L`}1eBsL>n{MVP=^PrlSC{kX6b-fU4zkfkTrY!SW%-|7&oU@^WD2Rv z2PSzJ?tU+5z z-QC?F-QA&dcc+x3bf@HjLw6o{H++Bb+dTaQ1qnMY>K(LaRo&8BSjRrg2qt=6yuwH%kgH6ew=<4M%@sYWdBy0z~nARCiRhT23}zK#~;e7wlKXpGSK37iJ4wEx}Zus@|^G)6Xt8QJkhXUc=aYU|`%UJ5# zZR_M91MWft@_Dxw}yv5wz5%th9n7GyQx^g16ucX$YYIcT5V8Dk}pHxw{Y%2P+(6p zGr91m*w=8;+C6^ymkg372l0d0(@QJk-Q$gL2qXhZDf=Gn6?QKVmLBmk+Ml<+kHlrK zVAJZ2E+q`qmhY``m2Rc4{8^}~w4qjxFAFQ$k7ynP8j*icj#pW0&9DkA@A|*7-fs~C z*+6!_uWrU9lb@PuZ3Tipiw-o2l;gZs9WIdzc{BxLN1S@${C8N%*Id_;Zb8ffrq2%FMs#lRV*gJ-5V{T8Oy-p})2+HP8yFl&3%Djxmb(b^4X3YqSL()8d zQRY%})R{#Wv{IYrDUXvW(#cHi{lU+W#v}AJ+_d3^2Q9rQ< ze<}EFW!*7L8yhkeDAp40j?Uybj^g67e7GLCNt*>nEEJF()AK1%bT3!(_G%%pS^Hh* zF?s1-WY&3Tex>=2Jg@%zSlzMVXYyrvMl#~d5Rg#X?$`Zi|D48s%uKyjWb4K)QgO9s z>)~V#*w75>s1I}F!)?;-;6JMHVQ^HV?XQT;M+P*l3+Fc(7sL0PEv6L6*CCv_DTCnCn66(LWWS=9;)tyUKByoNK|Vgfa!e zg+EoWA`MRJM;w_!G1%UCDBQ^cNmJIiCx4CL{LVx^39QcIg}%vb_?kqU{67{a&oXb^DY z3*Lfp#g1lD2;(y=1<`|61|z(v7ietX=nlCiIlDAB*eqv`{B1xVaKu^UiQXF6DX~}< zT5WM9L@+(J-LJdq8st_cg85;`^u>d##?mzkJHy%Mf|RpK`wV>Au-@`kCt0h{$pDx+Y!&V*UR z9UfIy{a5$cEE=a3L%V_l2jP6u4Ej@a@P2V45Q&f(;C@fWC8iJSyC0P|Cz7t$<&Xon zuy&N^rlu!J{j{2w&A%X9lYCQa9PO26R=UkhcJ>p_!q#R>q~EsuD(9WPQstq95y^2? z?605UWrA5riB;FgJh~)CS}h9g_&dDG{;|6H%0?Q&h_qB6`Mkd9K@p&_Z{TOl6UuCH zxs88JlH&L@>z$lvOH7kQA3?^)G?i9v9^dHxagTpXpzvo%xJ-@9Hzd7L()ZUoN9@v~ zJE{)knZa%KNnt-i-SLdtSv+bO-Hws;Car(;D0Xld@4M`&y`v5lcx@<^#b2WlXT}n( zH_Sdeiuk%F<#ycaCMc5F&LOJbE#VMaO1QepjLbKyIau+&_#NXN9Qxx1$<+>rRxL+( z$RspgQq}D2v4nZ6$fQ`7O~5bq4$3+L!Tf-3FTB*n&th7)Xs^&&7~=SeZ7&pq*pw2bD_q7A|grlvlC^pVe5#QRHTSzF(Rhgrzb#Nw-)5cak?^(R# z+7WZdWBc+3DJ#0R57Aw9?J-d$N8)(4k{9p1Em6Yz1J`$@B9H!fo}EJx5|_nIn|NvL zhqZ0qf|ambXd~zKD6uEr*OR3Sh-P;ho4?+VneSPv;^DHGK7Olm-Aw%w%bAP+dkm3p zOYz6^3n)#&U}9ho&{Df-QS=f?E>4n3!ZBTzvh>zXaKK@7+^Tjt2R+z*%EVanHg2=X-rzNdr0?LtlAWUZec5A{g|)E<_U}G}GbOURl*kZ)I~Jpz^LVc`$!-fZDSm zLlv~+JVBjgbJkITJ=*Gxt+19YP8@TU2nl}$&sr||+KSpjr4J3$wDULY8@$epyvJWH zNEw|&u~%|vB6UKAM|!8b^!Vw_$fP^D+r8TIL1ynfu3_QO3^QqO!x*hp#lqhB7kc(A zPPjWp@rs`8sLcK?vopd~10$Cng87nP4lqK0{y97VF>yke_k;f`(Xqk)D?Sk+WP{_a zEJ-tEtWhj_NDoFFV38*hk}gQdp$7Gydt9Ww!u~g8P%;dmJmpDglUe4-s3pznd)7RN zL=#ru?`u=P>ci9l2c)yJ%FrGCG3zu42A&&QgL1Z-BsD zzyxeK<2QgEcYJ6bLD>cZ?{q(;rJYamvd_QW&={(K%$=Q2h8Z}wcsJadUB_+S)C5A@ z*fZ9@Pee0u>t1&*K3gaCq*H3 z{O1D3v65(uuU~wobopP|?Dvp`#Izzw1Us#inv@za`zvO{4W``GRR-Un$7|H{Y10Po zaa3yI5phTs%%!uyL#%22O=+@Hcbyx4msNjkN~{9I)BXp0mZgeR+tml=w*#bR{05%; z^NeV($zp?}cB41S1nHKP-n*Phif+ZlClKDHy_zDK$o}@(&7MyH*Y)%l*BrZ${Mz-R zq37%zI!Y~RA{Pdk@>rW*p8+FWH(!qD{w4izf$%QkQI{_5NdY7do9sOK?!9C*fz z&*z`-zqNLj^XsF%T($R4X5GkmroZ=oFA?8K%SP9F65dMQIs4OHEYv_r<|W7Nb?I$t zj&{SAy1XOZal_?}Vt&G1Mo91|1??QOZTC(aJ$0$<+Dla;`gUOEhlAmcwpl&sU+dTb z+4FHbXO`<>|Crf(-v@5&HgvNyu~-ENhFaIcIE3`Xe7`R_5q=q(%;dS31V?wt0`e#+b#BrEHV1ev$57RjtlI4+<&M{Md$gW;lVOr z6T_Al2mZ*!PBq0zXRNv}flRL?P-+4mo^AGW$f#wu(7#68If8erIOnB`akjo9?Vq=W*-rMy!dLBKAn}2Xar;uM*P+O&r$2qouNq z?+lszZU;hBW*_52x&iw`!BXz?EX(|soVZU!#QBThrehE#wrC;_J^LAv8zMGW| z;cuw|zhlqAGjo5k(DNO2920mx(^8`FBiOMltZiqF8aYXS#G-Ck^PwYi5T#v!a=DqR z(?|UH$PeT8w@a5ea$at5D-|l6uH=j9@HxZhM z(y69nLXhLSz^px3WJ$3oVR>)Po*{l_Jszziss0=Kt<~3v?hKY*p53f5+= zTqm#a$_1HKJ{~q-A<2l5f+YK5$KkJxY>BM$&pXw>v&NkLW}d7RQ#NOp%dO}C(&Hgb z3TY~rTDpx+NqoJis?QCVQ%Y*{h_KI#8}R|OWZB)s7iqF44o$rA(|I%<>Tl%~3`ZLW zNAuaoB19x?sI;JoQP$v!!4yIDZUpjjMU@KK@`ghld)-&x!q2K!(e}thg6KUn?+Lor z$>2qMo>UMK;Ds~@of)WK0CSBh%lmF@Qa`Q61A2a&uqU*yQugs|xzd{-c zGbwVZJGp}&Jy#nJBpukkOkA#>zR5Wr7pvp%nCQ9RXL8$GI3}tM&G>UF6e0fVLK4?t zEE|Q~UX~u)7>$EEv@BP8E=N?IcdU2T^(Zf9xIl$ZuZvz zST`njde>rwY#^v_*hHB0J^38m`bStZ(|fK2RP&PaEBf&q*Ve1*6|3zv5v#aurcVu> zj73rMU~W@>>P#*zRWkd{R+y+!2%=bflF!lJ-np^qq;eU1%s!hRG5{-C&mKLMFCK=a zXZPu!styegCjyziK1)_A<61RJDk_Dcp`nB|pC_$m+jW0?pd!*|$VdsKvBUvKa{HrY zj{Kq`F`(?D2xNqFv60}P?FzxktwGfFh1d;WAUbK4bwxRCH{aVIIX-R12U_nFY;VgX z^nK=O6QQ93QvE6bp=7V2tsxmm#8bk5@3L~Uq66}VbjA1@c|yMaEsmjbIU2d+G4GpgOuwtSx=G4pdyqLefwWl5xcwSE`0rCg`h zBo{i{JXf8>|H&Almdrc7g{^&|=`1GuE}?j-nqsIyRk*MMxC*9?Vop6Fm_4kJSGm@z zFK5dKV_o}o!Y^7q@gLwcrFWkw+)BF^!c)B?<-U> zjOG+SVPm98DP3`9j*%V*dax5-<#6)t=kiQfC)3X~m)&)sq{kmFc5Xz9OaAcS5tZYs z9I8odPFx*M)Kr;iVWS!H^mFAG!-wi*u3Bo4;`KXSm)T@#QcF3lASdd_XBTXn9(d=4AhT`A%H zE`WEY)AP!k^Sres4OO7%=JcdU&T?F6)`M3BJj|~9H}?e zaVu0`C-ticncRHT1;g4S)tMJOg!-`IVwr~dwaTkQgor?o6n);&l(af=bRAnv66`{a zjU%F)??hXFSV~ME)I0@vp||kQ@uo)_(xm@_)tkzwneq=^aZVyQ7DiW|dABGuy416Q zMLN#)>E0Z+C&c@h>n&4xs;70@?Nr0F}os1u9Lo$4?eqNt6AD1oVVyF8jY-0ayI)9fS}K`qV-d~sV^;UPz|a-vI< zi${LfB$_2HMbbiTb=UTE+->czz8e3A zdEJa=LX}b!8Vd*Gms`l##)?ciq4xz*#C&~MlAHrM5}$jW>usabx%*+QZ@YvdNElCS ze&F_iVqQ>F0yLCm3dFpB{H;tD@G5cPn!L%v&+XMpTh_j8=YAgTX)m+F`MO*Z+3OCL zU^$lZdT_y4`yd(sQ46<9*~6NPsq7%~>{g3u&e**_bgG9#5!V%cbZOgQ^lMDy^+ve` zTWFYg6j+vSX_p#zTKsWPd#M_*n_>vdO(OzvZpb3P+~2)1nYjD?V@uZu8Eh%c7CKIG zMN`;SuvcLVXJ_h=8zdcucg13BF3$d_?6{|t2Zn-OXOwHnXM32zWonOBM|A1Af?+gF z*&j2=qcIEGK7x*T0NDXW-sl8T9v^$>oT7+|W6){sgcI-D>-5{$tKOCA&c5&nQc&vM zHf4=;T9d9a;HXDn{B$XubKLqNvOqsQ?Scbz@k)EyQ1^ZUZS=`{-I>pZ{Av3gxl3zq z&R4q?#jB*Dj^!zt%gztNUHa9!)v6S9bcJng__YfW3zh|dNtATCwnWyogeD#$CfNiq z@Ht(M^0bvo57)A#`1zq+Ru{zDOO#uIazO>s)Q%mTFgf~#2&+foo|ZoWx!_xdE>bau zy;Kj1R@^S~kG+Wi7($9qUc_4HcDjeVjV+{(0Z7N=4sLWu_9}iJ|L3q+l8~M*0Tf-P z!|CFuUN_Fk6-sBGoSt?Kmy*z_Fg~Z(r`3U}c5^n)ao1q&YePT>vwwdA6pDHIWJmTK zD$r+eq_!W{3j1$%`2vxwJgBIMti(MVxx{X+aWD-Z!SYzTGaYLo4>u-~MsE?};Z9Fu z$ag*4aheXWkV+b6tj{lZ+M&O;-TXbgnK{)L8504@sFanLrUA2}Jr$ezNNRRSIz)85 z*z+m*f@!wtm23x>6aMKnOT&*DTY;V-ow`bCetjkNkw6CXMu; z!8WapHvT~XLC(SmpJVKH?E{JWG%SCv-{T$pgFf{pUw^q;y)&Y7h7qV54M08YK>WUP zyRVh^@;Gv>@vLO{x>syQLM*8_6X|58wrTk~yh?CnTsk7(>v76I&L*g|yp~ngb`r?} zIum{u??ji05A15OJ5$0Nz3M_0T~{VbV1wLvW9sUoi@FwJ^&9=riF-o3zht*yw{Ep; z@fz<6Fx0!t;H|)U>{z9(Y`*mY(I={@(i4Y#UfeH(c-2uNKFR!!0NVetBET6%E%@-y zH`jojM{l4UGdua0RUjxoCO9hO6H~|qb3`!!C<$=hRV-kI-E6T7U_KGZJtZ~eM@tnw zx4U}_z)hYfAK}Y+MyOm--B{0U*5-kuiuQWhF5JMau%P4Nn@X_E6*ht2Tn-}U*Ff}-wrp~ zScM=Jo3(1NvdeP!APE!5yHVD&t2;?B>mL8W2XW$h1zb~RCrBhdgwet67-cUZ{A-~x z>VXpLt^85W&uAgl+3O42||;W4?sEK{LSW{vf~skig|`SkH0^J z@oyM|_?48Nvuy|;r?(VzpM#P~853`lu~7Mb+hONOFfhLVx_e&NvQQw(dj8Ca7d8rD zxw^#udvR)?dH?qrVGx|nRt^hpp?-ZbsXDxUx1jp_pxgwz?E#wx?cHhN0$(-6G}ORr zBG5hxr*sYUJZyBtV6~rin5B#%y(1AYqZxEA#m}gvkl~+zq7FWlb7o%Eh=0z!_};gx zitMk75+F687C$FVMujnq%;Qt>oi?LsT2tI=Ecsy?`y%f0AGl0O7X;l9Xnzjo4amo5 z6IkuYtr>r$#vRJ@-aJy@_8GHoVYFH*94CQA^}X7dr!R}WAz*ool=e#`8Z>VbGmski z&eZkDZ^_k$pV;S};}daHi`T4KyBdKPR%0n1=CR{i)g8Q9*}d3GdQ(zAFNMF_VWfe z9~wJTscAd+J=<>q7{{`XkBiznS@nXG)?n6>eIYpV<;HEUXbfxQZJ5IHwM+}A(5goEc_$S%51h~Q%Bq>p z`g7UMg!EGXKg|x=2e-U-ln|E)@!G-hemE7qEWuM35rNm@bRps5(r6b)_$c&YXy^m) zg2C21KAT5(8Lvz8V84&Zr&p6z)Wf(po&Zwk#`Wt~f9J_ia^dYk|C;M%=@gRY6{3}r zAVDz8EMuOOpz^~B9#4Gq*(o6@(J{j$)2)g32%DV=1T1AZQ>RCOJAJuR9s!eNg5!3- z#I&ro7M#?$*4?_6>2yKLdx#-Z$@Pu2(0Sk0;^toGsK_}Ry)pBr!Q}hrL6;4=!2>W3 zLAR+Ud+v>N&&z~_)_W8(nGwbsca=2ByCkg_O z`|)np(9p0*1*qvYiF-37okFVRIax2at+fb%$8GR)+8Y-dP7WTm1NP~6QeF#Jiu9Ss zXGTX2OcAP188pG8)vmS%gl$V>MoZ7{>>ZN6_8Naw293c*8oH!76Y8w$us`%y(^8H` z>;8g{wh1QRj43Qv&&xN?M0DH0w#|#P7%>MU2!p@2DGa$d*IV(uy%9>4@EZJVSE;9DG@jWfGP|CK>(oz02`79u$acR zIJFCs0H-%Yy*-KhIJt=xlUnTA2*~kh`5O>PEOqjw`}#uRbg2HP%__py*0g}Jx-4CX zWId@}0pRF+b93`|TPZeK!vgS%+5A!QT!zeXPJ6sG(|#)8Z@)niO#L8kmKyk&{m?zA z&!o}Mk}Ky$#H{dzsM|+uDX1)}L!w<()8PLBB>cCdj>vv^WNMZEb7_HT<)?>Z8}5!f zI0k|THF{zMsY6$ykF+`eH*n$^P4&-m)V)GP^TT5WG_KUD?Es$b1q@YP4`)>N=6~`4 z1L?vu?=>YQ6@B_2a0zW(Jl0gJ3mpKM_$EH|)}43qJF1rjK)9ef9!VDkj*9N+&qK6Y z>UH?-u&CR}2*ux8BA?Ao9uauj`@r=lZXm>_p>ydJ^lt`g?L3y)ej=NJ1<#qtM4+d%8_<9EO`VyLWfRo_}bAb)|wIjpZ6 zKqTF4gwwgYKe(cS{%rc4*d|ebGag;4rNhMlUc23S)6x?r*wrlM{#eKDpmwvjjHfI5 zmR#E>@4Zg{vQ&JK({ws;@1~>?k(wkafc_e~RXq1n5PxZrCTTt3OT&T$CLhg|2WuV-u66syiZ6@S8MR(=n;+hp>n(`=>u`f@_2LuaLc5}1Micqrk@Fm-@d(} zQqH4yJwd&@2;Dm5coX4q_6@*X^;c*;$=Q$quo&{0ozWIgo(7!2%6buCMBHlp+>+`D zE?$9cQra)Cpbe;E!m`)x7lCg}5*nr)7URvYko~#q!*dtR&uTQ9T$42%4xrVvt5d#wh!R>&0~{MO}+jLRytzdt;Y z%b1e>GUPwMdU_12=0d`6oZem!=0e@9tXS_&=qvm43gd=(D^+g7a4cf`q^xiOZz*+E zoxO@Bb2|_y0^1grS1Qo`7f;tzU~a@eM>v9PO*qo#cKr8Ht2HRob0c&DXvozhWc>&X zre)UFc2%n8yToIb{nsz~`Bf3&nJtFF$-irwu2+~#z?qg;?;BK!%RKnV!I0wpSAV5? z3JAn-o!O8n4QGyEE|ve&CBUx7spN&^tTnx(TT!Vj0jO0q=O59$+^#ok7q2K#9km=Q{I?D<9=WP$@Qm7jj<5h(fgwo&FYi~z2UaMJefeN)>b@>IrzCJUW zHiX7UOKp_*_X>E_agwb6j3Iks~-Jn(ahf!yE1aO`?{fs4K;_6pcNfD zuZ<$Xc7|fJH4-5uIg|%$K-0%o@PmV%F#M6@VoN98u2?0zd#AmZM(B7rHu$<;B2iE_E+5_-rRl`?vQ6NamBNEw|)^M2)9^-ak zZ0~ZhHj3)og}9bfxBIt)5Q3Al0^v>Lh(nm+*)4nGI<8>RfTG3{rT=w%J8G+qb)P|| zG?_pp$@5p}um_ zrTH?JmsMK7H|Hb`43VLSc)OLr{TE|wr=nlV0tMdAhFTOF>E7-84%(!%(MB-%qW!gZgvJ_GMXO0MH#sLxFKbPJxw@@96K54L0dstR&Kx%!B zW2vU;=GRM|;kSCFaaRFbQ3?dg|N1FytcVf)YRz7d z2sw$ACUtKQ`~aD;n6XbOM%hmMj><}x+)f8y`rl@6ydC)SCto-m6Wo{lEmzxC>;FK8 z3jRA9;ie~${Is*pOdRu<_)e*%>Z9d%fF;d{_@CDVzBZ?Pv-^J>FrTk3MDyuC?f&oGit=Adga7lIztu_MISuKO|PoxX{iluzDFW!uw`Qh;fx@ zQ&l9?$pMAspJar}mG3vm<6{MCpy^HG+SYM-nKD(6+31*I<>{f_-u!F;Z6HieCsvk( zbrN~0r1^5AEiK-$f)}sy$jkam@5V~QHZAC8)~@80t|ZHOK-$k8y)pC;>a&y_@O_`t z$(9B}qwxkQ3LyHevqBf|?1&vnyTk+xLBUcLoB#@ZD3KB=DlA899umcS353T)-Y3vN zFbJ0FWl6})lu91WM3`uFrsN^o>k9io1-Qil-w;JM`_fp*??TkkXbQ=$1nK^$MJ z^?}1!`LrqCz-I{y2+_cOe^fA(ei}kn5h*5(BteDU5%CODb{tZHAd)hgA z)y?%`blQ9?lDW?jZcc`Od;(CPDy;o##hs$b$x%<`hEzQ+HYLW6`ljJx-77PYadPk92USLQ8e}RH{RA3 zzAYTUOCRR2+koThTpJ8T#DZdX0%M$`p}TFI$^l63BK4MH#q#2`#-8C?=|5H0@}AH%{9lYTI^i=ON;`t)h`b7w55@(*i6Jp26P zaDpGhj&ul_bn1=8wCq3tIL+#pV=c;iE9uWp#i74qCxl`|?ZuN?f>2eo9xEK}BSbDY zhGwD-?K)oNA8>7`m#S#vxqE$*u_G!!MC6(aBWyoJ{A%r4VI@;h@b|{De=VET;S0yr zoY*19^~+RJNqU))nsR-8eSrBUMiSZQXYL)qKTxeA4z+n||0BnHXZqgfaO$1-DEVvqcQqbk+DSAz(jikmk0|); zzc%{92iUMR7n^Zkde;3C+;=gzrhB(=Mcg@CT@K|{uzWC}UC_Nn z1w}57MRy`p-;ITRx^Eu6n1w0G{`M#ntFVnWTe;AMi4M2CluA=s3dTAxxVsW4yc2Lm z3+nj9kNUNtj&O|%_oS^k(B)0A(>F3AmCE-s4Q_9~74Yk4rAsJc(Njwa3$(cJ5Y~9v zb>&lkme;y8w|hM6{Ry{^#V=xArrJO!%?NGr#!F-A{{DyVz8KsbO-^B9W;>M>EcCcC zptvXnBf4B+gE&+;cks58ayrZ5CEsUfq7MTQe84js(cM%rDf6+CK7XxYEQ$PQk-@*(R-vH`2_eln zN5w1DyfDjz-Dh$Hrgjp-kUTw?Db#`GYxFpDI&MVr-cO~H;iIe+vR@&~*ZvQqw8EgswNK*eg+uQzsL~q^WNNO|AK8U64Q8 znXq&?8jpUg&UH%P8OzUIC9!gPYYH%w1&L$j-08_`of}Ky&@I;RHurZuJbqVJ^8SK-{h5g}Cs2V;0O!HH1!OU6NzBt#fA}M6Q*5=87dQD=Lv+-+ zz6IB1)%|)p*fMFs`}9ruRw-IsneTj{ygu|CrPZbsUkfM{QiUe-#>9xzcUfsA z?^-^+p3s>^eN491${ZwF*v`7{)_FpR|KcG6rAuOS*%Wp^4al=_nSOjZs+l23fA4{Y<=GTS5+4AFC-e8Z71D-#zf%62R+oR8XmPLha3VgC$R@yUMvHQH)^ZH?)KDDCChxy+YJ z>GXmCm%Rhi^U!`#N`OY^0Q_jw4kS&GNaS<#^%lg(r$G29!6v+Rr)|XlIz^5L-l1xrly0HHJ1w86KN{p2vIs>? z#H9P9qTW^~J$N#%ZT01ULqpt3sm+Q~`Q{v>7C>1Gv#oO#-Zc*`vr^Q3lZ{fGl)iAk zHoBu-w?1rX)QY^)3@fJ_wQ>h1I}Co4?dHCKmA!DVC%B9nnc&jCPd`Ym!N8#p=AI~f&P=4S6R+yN(^IMQQfwA#>z4d&;ae{!;(@@!= zMZb>~1S0F7^ZJwR5FyIFywX~x_LTNp0Aa)F#G_%y!Ip%ZL8P&OS-QInRtvwsbd^_I zUTG(E?I0|lOA3aZDwZsYI*MQ1)eF zWAOPi zuyAZN;^x7M$7Y@PTsdzpQ?E2)O;6=w^J@yFv%eYt2ng%+0>V>YwY3RPPvdX4HlyV* zURm~Nbpk9pGP03%ARU)8V@}E8lNAuaF_OhHoX^LW(_L#qNkfzW6#^e+1RUlUYFA0@ ztYMxNH|$oq0Y>HEXl7~z9#<5QRcx8g1a3AV!`-z|+lhw77TW7}zRRcUJ3PJuXe8a`;_}oL@}86f@X5^;iTVquIXF%`H5l%p0m=^&fFUTOXD`Atbq|q_ zd#oT5FEl#(bDb34s{208Q72!7(J-%3*TZd;1Lz=LhHpKbu0wYjuuXNGr$>T^t?b~G z(vo6Ns4~)l{wsPcyN?PEAclw-!!Z zYkdDsgwLnj9EQt##r7Y!^-J0a0a46&@+HuE zq@l1<&+_6>*yF4-?DtE4-oUGf%9SYqg`RH4K?t z%8sIq56KuUlfZBoui^z>_s%va@}4E}?Vw#Ao|6!bBj@q0hbq0;>(M*Yh69EDjyuWB=b!INwnTl{O?4*uV%hvju#f2n8;rURNA(HBK^|*5+RiB9( z!*u;i{vjkiixd2u2GJ}*;k)6VXzWngyA?y}i^;Ck*2;f)h!uEXuS9@3x=`&ooJ8}|8m(5|9}gE(GGRFsLnc%zU#tWK6RPfy6#!an zIgo974kbLCkDUz?Tosz4%=yJRL@D_HH-7n&yHTtM;7ge{@ z8N-8-v^0VW4B8zi<&-Z({jifqz6uvhE9VKzP3mh#`H^~zcSM#WdpK7^Qc9R+F#2*R zS_{;fVDITv#0iG2?sFn#caEq|Zx%;Whog{IGAuQm)1*UhD3&1g7!rMUq`=`n#!919sL*}4wPik9D;=jK!ayc}2h_hPn(%kRB7rm?Y^9j*+Kl$&f09IZRe zA`|KN#CgX#uaPH;ZU}Gvip4sj5>8CMMOpvQtv%opr+YI;|-NFPBzVAcMKR1=v!oEsdzZ3On3iw z>dX7%;%vFA`;M~-loIZ17O#T&r+3l{*PD|S68}O|4*gLgd&y2V35iClcd}HRo^=l| zEmgMx8XFV2*~CI?i}?|R5I8^-2r93z(1DCCxs&^W9!UHHN)AxtO?C!%*Np{>mTL`> zfFK3?VtFxOZCDG)YVFOGVy&*OW^g)%rP_?@4*aF)SZkg#XvhOxbNAgIZ?TGW*3yI# zQStF5gQEzb2Y3#6)lvXf@|bjruQt)fd}BBuyilfAn^sf9nB@+pe7q>+I$&WB6qBTQpWby`baKx824bEs6% zdG#fJV`F!w_H&^$P6|*uTgEY!A^_BZjUjh*u1y{;-HY`O3y-TF8dDWoT+fLGU=b+< zx?$R>_*wen%@~gkFbPMGUE(}$=r^-*WB^LL*6a!n_P{QK!NAxUD46;>BcSVh@c56f z!dE8{Jv}%$W)d>81Z$t-6gu@n!0^z)BtnGLWVA^JgVpvgak53J)KWtfwe@<5E=XO( zl7@tTEpoeEiP8uPgW-77hk>Us0Fl=lY4=v;s)ILej{w%p)CY_DIH>Of+UnsR*O&DZbZ4~NjZFn=S;yj18OxiepY9f#lZunAA>UvxxxRVB#2 z4&`>cRXSIHAgG9^#A=o;Oc53cdkV(EJO4&Fod|H)-lW$C+x%9F>vN?|tl#(j8Y{#O z8Jt83AxA|YEX{DhIv*H>!5%&NN_bb16fgV^m6aT~9mE$bZSL)TQE6>xjWwWQo~bb3 zX~3tgmKNCMrHfHSBz1O&4HLO1>AuU#+(EN@YDi-Cnn$VOJchDFRhBohtVn<0Gs^f? zG}(w1ky8L)-fN_-=1fV1WAxBbqjcR3XEoV`ZT5D7KlN@w>@)QrOg&^ayVr7&&1#+F-leSi^wOj&O#Nc@x#++zbhEzve+flfD?wj&WZU1; zUOrr8i$x4S?4Y_P-x0RHsr&Xf6I;arVxl@{8{4wA|^NIfIwaoeTS3( ziCPmSN9R(y;xQu0Pv?gsF{fSy0A^qFXo zDw6nn4XDaM|Clz+X_+P`>n9wE2fldCB1!3#Um34roz;>M}4_`;g?8l@g+L%XWG!C#6f3bU4;-tI` zc8tpi6ln6>pAcemX$-l%xCUej>fV4XXIeWLnvM@^#m#U1u^(p@yS<3_%U$Kmd|Fmg zSo7lER$0S!j6z38F)=`q%k(^%zwolUIy)-eV?|kb`FPzo7-=w!%k_?$5FV}z85|^m zBo-c%^Y?qF(*4emhe^#2HTJ{-*1$Zvu)@k!6e(8{j${n_k{_iy3sryW=f5+R2@Vnh zXksnzhQF2uccWvlTFis-<$`pux)rAjezSH56gxMvDbdsjnZVi86(fvZJNLLb@rc}(>5WbYBbjPLjZ90P!?Zf1i&5JrhYT8}%@f%LQ+^$MXqilTN2plZDe>Gvp8 zOBV;;Ay5<;kxJ>Y(~zzE@~rvxTq{_6;!Ul$n6D zE3TroAJCw%aIGW@=2?S-(8gqy`r4XObJ2O#u=yHK%JxRg3FfjwmX!VVm{-h*Ih4Ln zJd(#m0t%p|ja^>l4=|Ci2u|wMy@^I7PENR}VpaRo2~|%&EOe(ViH(ZSAF{C&gqKvl z$xiA15>ijQM|g#WB{niVVH!;ym+&)+Y}afK9wB2$8wJN~wkDW;?s!BozTRqTf;$4o zEU$h~P-vI}{+cbejdm*O_Jc@Oxs^{QrkKGYyc3b%O6n|>dCIEM=(xmBE4=z<|JR#@BceHssQ;yit!IV4t3;w?eJcOg+3(aTNTjvhnht%W5FiSYXKOlzH#F{#7f7z~b&WA=IFNzcTTQYNyx>1S< zBe_MOz>^$lRyO~)MeJpy8iUq1>7EN)lK~B8f&fT@^2?WvbeoQfE7aeBJg9JCtvBZ- z=>BN4ey@@Va>=$l1nkg78jTh1=fn2L?bm0E7jw_DchBD4KyjvIT_%`;p{r3C~qQg*16AYkz9g?lX0~Pe{K7Hx5Dml2tsYj)>{A?cM14P8k z>`y0(PdiXRJH43N#No8W1~e5v^YE-xk1j1C+Su5DSLKtn+dVv}QJnUJluMnad7Qur z68Gl3yiW9qN%EG6QKcWSKIna+$)Ct{DNzZv=<>|iCMrNp_wuU>eu}>AIkFobak`1m zifx4LJR&uA!))%J)kNWzvR#zY5ck%YwZ+LT z*j9JJHNJXh6kErc`1flpT<#}uwU}ghoY->mYrihIrH+&LycF;>F`OmA^-@f}Zv;h1 zc-%+HP-v=i=`6-q-j;8Q=@3PZrU5~X^yGY!nwH!@i2fktiwfrcSRKy z6)ZJ-M#`i>SE8Yn{V}7qcEW$5b9g&uz&cBtUHfa0y8tyYHi`#{@`1f3Li+JSa2ALn zQ{aq0D#KC~K}HR~(HCrUQHJLzy9F$D(Foy+(qDTN_7lVv57^kC6Z36z7)+14ig>HO z>s0H>!Sn*qJ>mdyGZTQl&x5k5)0_KRZ3a&~&wau)#>;vqO0Am5ODu%`Nc1Jo)1S;d z1pvb;0B#%3ZQ*w~`9hp|Zc-;(T<-I9wfJt` z*0@Okj<;6kjMgEH36S+lEp%sR8pU1B9(d1m;QvghnGh1;jH!=)rp>3HY~x?hc5Nm_ zHeokD7IeS8`n0h);bQ91{cG5TA=DNgv`lYw8X|5c*;*WfpOvy6M6SGZtK4YN2XtWe z(0AGF*iZ0B8aLHCG|C-C_`Lgr3JTb(i;OYuc%!iwDvINAua5G{x|%+UU^fm;K|vHW z0-{l(vSP`sYiF=`sMoc@#Eh zr*vCQj^HQZ-kz6|=y33Z#!_ec)p91w*_Fd?%q8lfL>ii~2HU6?hUYIYb9FkAB>Z`| zvz*0)f5}}DG0-di!=8SoZ?YT8_6y4*)5%jix-!c)7djFFpi8-w^iahlg0DUpys@CiM5$qvh$k$YZVxy^@X zR+RQVy!xXBCORmYEO!W`?I4PYDL8R1^2${wVNP&s&M-9M5<{zs zmiOnqB>Y0ZM#!|e*vwTve@rWJBBIPfNY4{p^_4#Omod)@4z^ikEcuz;ablZ87SdK+ zh_KyQjZMY7suPV?vsGS<5A+j?lizt%FXPrzB^nM7%d#-$e?}bk{Muse#Pl{8PE@pf z*CaHZjaqJFWNgWu*QE^CdEke4-%-Z3+|OT+X~oqiT6oJuj9!@8ur-8P{H}5^VW6~)N|#iV?shea)$%^9Oz0vvw62$AV5L(6!ls@{vGXG+tn0Ei5kbtu9A)V)x{+t z@=0qMU@(_PtcJ$wZEv@>j?N~J+hDB^x&aLo1Zj3Pr!IgSdDSl(JmhJ7=oC+ft&_tY zY@+Hxm=sWevuUzhRM;w`KId$dfgP@0noJBIODfhW@~Sc=H3r9!ne#K*pI<8;K79BT zoL|2M@+(0=sy;mbHpKP;g#q z?dmsIICRU?715lW>oOda!;vFgqLMGvB@l((Z_7w%`gaO!XqLpc@~>+}2*h9oPO62$ zf+HU%{TD`m^^_7_!pz(qd{18T2v%!0u(^{A+(eCZS&fz8ZQ6OMm|UgvdC=MUMa4O| zF^ueYS(8j{WsnjDCxhD%30w}yVhPt|iv_u0Wn9l+5%YMe`nRO}AOd2I@ja}E?o}q{ z8BR3qmRazL1T>}CQq13cJV<`HO72oirIetaHt~tPB@Z-K%CnAa8t@Z{mj}^9e>6|+ zV+8%3(?W;itt17T2{mr?{tkWfN3QYXi#35`UNJ{hujb%EWJ?Y&{o7NjSXWCD$Em%Q z4P-)1%Bv_;D_0_XHNg5NLbwc&64y;=}O>7y-(8aueTRzE-SNdI}O)=l% z*f-TXdUj{MoYXYXt4@KC~0MbiMFgYorO4;a+Y1O9zN0IMuhjsTxVV~? zY~J4gP4sj)?v0rlcGm*cq9!2KsRXA6z{|x2<|A0FW`%jrWhNnao=_;-37LG>av=b9 zV4N*0M$(Fz6JsSUe}3+4j1}SefsNAFbLI)uP|SaVo+A z3Ba@p7gfKvG7t#tQU<_z7`C8|dShWh0chO|NnRfk5{fw2fe6SqSu_!#eE}+ZKRsr$Hc*8^q^@&c^89(fg?KnZFV;Bu?mR3#o@96adhO_j=Q%#<%$|u-y;qSlgb*} z2DrbMS<3YHl)?y6bRAqO?pL_D>@Snh%7FURSc9*`ZGj(I-8Rdo@3)5b`+Qw?*}zqX ziywuwb|~saL7qOv1%%lYeQj#Kna@i^f^1>f?gZ^xOL9h^(hy)(hbX?=(ZTL@j<;EN zU;y-%pc0K9&Q=%Q>2@o(I~^U^FS~0`S&&6=?Ze7g5er@`%;hJ%~a%T?E%9&HK6_bDKnAK2AWe_z8vp!mqkMTM&21OX$PdKbC=1_wu_TN z7p^jM0%W#js+u3Qz{BuLDZC9V6K(!c=l?;J*+Pi~y7JM?d~7xDskS!t-kJ{De{X+( zxmRQ>-F~K?-7r>a@aOwKM@N?c-!S~^-4YCn%+KyLxc0h% zik0Kd8rzWNVEPr1szifSzMH2f--AbCT87>v5)u+Pv`_51Q(MDBA~wP`b3r|zA6)CS z?>XKbmgDN$<;ZpfB6zz`6nlVjw?Fgz3XrPUR}`Cd(TIED6Ot`+W8$^s68H%7Mq$3S z2Mo;2%uyJSq-m@HM$%kL?ukEtN}1PgUyfT?ICNRp)CJNfProEF&)?$`=kg$_t6k$l z!d3fAluawMKM!T7U1A=o$bF%No$r*lXjNPH>wHgeNVzk4DU;8AW9+LHy|~B=E}(19 zr>1B-&5nTEz;h&@7)Si0V^(F8$M*MwY#HTCpb|%S>+StVuQVM^^?&bYVwFC)%tTWy z6~fjg=_F&?X8s)}GVrX>4T)^g>vb5GBKq<;2y$dhvZNb)_uL%mb%#^d*DQBGx1ne*?gwJ2)hcTkPx#tP0&Uyf@=w3Q@Tz4eAjbEHG$)HR8!1w=+3O04W*r39|@^ZS-rQbKCKyNE~dN!M(QrS|K9*a9``JU8} zKGm1|08182c~HMmLa3s{DgZ4b;z>CceWE&U`(ki~7!&Y~-k3 zYw#I|n^}WL9)}zE8gDah(BmX*qKyYO-b=!CO4INt3eBk4TDFv}x6TAk(HYyjmSz$X z27lHWjR)66U##Uk;blarz)3l-PYQP*c|ydHyH?t9=wg;X>s72$ZtO6ZAGrm3QkYYbxK8M%%v0U8C4dHFV+go+f-(4{Q3#LRC3itsBzG zKa{4$RZ`;HsDFFUFe+91hnaV&hiyiw_pR6h?U%d@z{%va%XB$+v^$?|Q;&4Dh9tpm zpEHJy{6T#)xO(Qi5B4mIR;WS~&+qCw9UnmDF=l4cIr(Cb7Si2BkpR2}-75w*!I^N9#S^-?fIw_pOq@j}0B! zf7~vDZ{BMp8}X~m!OeqQ|Jwe;($F1fh7Q-Y#7 zdDTn0kfs1upk1Tb=iONou`-vsqHW$c7KvF<8`3qt&%1Q@-Jq8f>74%0j>#+6qP%5o)Q@#hMf6qVOG9!<63D#6 z9p=3Gj;cwQ{BJ9B2L1-~rFM65ZL=UqWL+Af?4m!T&wK%s7* z{tN2xyl%F7NB10Ro>IO5=m2}L2s3t3r4FVT~snn96Fft zOvGEmyJ3*Yr1sfI0I{X`HFx{PJ^8c=ibV*jZ4@R-wi?pZ*LvgGy@KwJR|QTMf5ESq z(`|rFy_0D1l})2NG27~2*x76V6a^z@Kt&UTbRqV-KRYSCu+JLgTAP=u1dg-!-SbF7f8T&uqqT#6J8}JNyuVn55YW zmw%|QjFE~)C|QJiQ5k&gqXxu+zIR+0Q3P1Aru0a^ZB~YUw{KUH%`SBM*sKi;ZwaD-5#jirv`uX?~0jcbWBoyUgHo>!OcD8<* zqj1cXFeh4{5gS&>!1Y~FD;b5pjA*J_9JvHzW29Wlua*QUq;MQh@>~ zC@A5Jaiu0|m?8h#E^irqE=?gfj=5>cvm7H_#dbCqy6ATGLqdt1*NOl`!p6-p@ubf5 zU(7&}J@OU^dU-@Y%GWU4bh>VMb6tXW9X-}$$HS}uSXEPCTM9zbDF`0@PV|VT zM2=xJnstxoaVC%GcY?qf`TMfo-S=~(XZb}XQ1`D-y3mEvC^vDKl4kQUFx!)s));KG_)J!xUZ$|6FM_B`_Q<7pv4T4YL15_oB=2Nz|;Sw493`-aX{8tt%}= zNfH(+ifVFK%q`^U(+<9T_K2d-V$I~m8)f2*L+%lW-g2MlsSTkwZUa6~BIm}RHV(Ye zbo-+wdBNEb|6>?A^^-g;oV= zs-(E*=D*O@a-8~1n)TZTQIuXK+VER$V>+pb^FX=P0Trm_QJLSH&MjI=5J+VvbSW>h zdBp9xUiaJ^kJ4iCIC@+?^`r&&C69Az`P)O29{TjoYiS)jd-yPyfA-%Njr5DKB&5r{ zNL%z1=#&77MLi&!4I8)7#Im?RRe$MV##f6|uQCOzM+L=x1uLs;HPANkRm4>pB=prl z&M%%1NM0@nJ{9sgsXSWBXIA9kLVx5E2RJUbhdS~nI(5C-CxyyZ+k-%Aa$lmlAh*@* z?BuYs7cMgO3AtZvKO^31#9hhJ@UZL1vQ;k}p%$YzkD%+a_QG%1k}#G$C07NZd5;Ea*pD zDfRVb4va34A1vu0eh>b%F$40pyPQfRTebM>Ws?^5AM6`Gbt!y2(GUNa5W6##FMC|J zR8*;V?QUmgtS&g7JS4ozmXEa0e5bR%KCc&*rijWj{a9AMpjO@I(l8!fgQ>N=?}e`B ziZW^71OT(JjJ4w~44nwPi?oOmo%usXDCQOfBU_T)>94q2VWRqRSNqwuFqac#{^MbKgJ%l$)zL6DQVz8{c&bDZssfZA+P-Ds3kLgoddcDCJAr%Ek-8sj>kh&X9@tNO zLR9Ki0Nt_poWkS>u}<>iw;GzMblN60jCxVupeS%}a%?gRhds{6lF9wm_bU%(++V98 zBTXEuqu(kFOJ#YA`$iWV#gy41N|dt&5AdHq3_lQ>`a)0+HC$c4Ad_JQw8PU*e||An zn86IK!4XL05wPS?0Cr6D&n|FD;PgVm=GvuukHJ4C_?UZJWvxs~Po!y~;oer4;GNIb zxc$=-@c@S_NxO;~E`uyvm+RYpiId|E*e~MW(7z$Aol?d%{1=S-{S8j_Qj==s@Gah3#&4a1e2vVmYM z8Fc&%3J94X@^^$J-*i5y9T&TvM>cA#k7_C0&&|fiTU{TV3Vt>6oZEGVT%c9<`gZ~^ zXBja8YCkX2>}r7os9e-WXF6Q?j(DdzB779-YIeuW>gx3#2gGDblMLzA%j&#r2JsAa@ADizjym&u$?2z% zY1?1={y7{45#1Uc)K@H;st+mAZL;C@eO~L{zKCY55N~!bP4ay>n)$0lF`jW?TTA|ByxQkW^wMBI8>kig~$#=f;zpffy8mD}rH{se% zPyf8|wEFizDx+B2XCXbK7EC_!c_Gt%SgZ8jO6lnFee1bPcW|4vR$-q)?Z#vjE?XiB zj4^It%5g`GspQ#a`Di#=Oz;(E`IH4E2nerp&-v{~h525Db0HJJzT;x2E{g!tLarMO zH#hXULh_#BuhbOP9#9?<>4qrYnwI6)a<`nW%8 z%49X?+KAAT-A#fnH90=K zptMC?b_<8>vbdmprf$SUBn%C{OFg2;aNyOw=rck(Ob8Cc;Gu5;_@i#{8P1O*Fs#XRWM zW7iD?n<6}D)kRa{^Wq*4KJ8HAZojRi*X#a;4w*qxj~tsUi%sThVX2RLn;U6=ci@$t zvRr|pWa;O~01WN4_vf)pDs=o_LZv>X^yy{(vZSF})1-WKS3Dx>1w~9piEDOr1r^4e z#*f`e^hJPYk zJZ)@Ln0`&Ca(}>`(=n2YR;fV&otk0Bct?NvyzFuWqb14a%=PTdAMkX%%mYTp8s*0% zHqhWLU~mSrlxmw|ph-e+61skA)&A}rGwD)Nk4r}BFoii;EnM+FKTE)O zAyt`Jg$aR_!s`?s6ICs*%KnVHo}DUOGx_sc{mOy=kBWKu>^EmGhJs$H;f_W&B86TK zIo`Y6So(mVg63nH?#W13&aB~#R$Y4FwK` zy^N?|BeXL(IOLM@GEZL>n6)YsB~wG-$6Bv24tFq{pd8!_4K=($k+E4Jy_X2_vKakC z^G_bb8n%9;U%%n(+RK|Dgzz0mc?G~Gs0#b6XNqW^R6TFNgb28Vx4pBYUT`BGkP(df zfP2#MmhHyLnZ&!GpqyO2xAr}sdj>NYMS4+P>IPV1QT2pb&3Hc9o@0&f2ClDXM|(T4 zB@rhkPf{AcRM=TDK1nd6e3gOAa7sZ@aw zC6u3S0O(No)ho;|g}MvTVg_VB@D7t-7sE{8_^XevGspGn?q5f~DAOs^MhVdOG$+J9 z*Zm?cQyO97;7if5&TFfKFvdqs>?5uGACKC-JQ$!&@4_dlX<>p>*rEhoHWZn<6FPsD zv&ccOttH6m!*dK)I?cwGF3`fC5zOzV@Chi0((UCCn2agCqr-tI#%GhVhS2n3sk!p( zcg9TaV~-CcANvU;glzMTm9DcZSaF6Et{Pd0_IY%#F z?TUAlm)j3584_i-H&zsiRo{pIxEuNgxB5beKksZHa3e2YFa9(;o!W1fiF1`FpG=wz zUwM4N#apm5Ul6s^31k#Toh(~lA$)Bl4@YebE+&oYy43}GvLJI~!v?goT{3NJDKzh~ zzEyvY3x3wDNeKuDMETCFfEiK{BE=NUsG_1iZtLL%=PjR-NHXWWF+4pmYw`@<>g(n0 zxRp)RB~sqJ$!*f&0FtpU)CQ$aPEH;?crXih53S|JXvi`NfrLDml&-_s*%)2N?U_ANFdsW4`THs)U2y8U{mY zBGnp={~0}*^581yase?rNW%P!UGeB&IYtoHSJHrj#b4X2V(NT%@#e6lF3XvEG0g0sa2Jwmz4Vzq_dg44j&vf6Ba ze7s~?z;3dAWk98}bZ$}$ZR~)-Fo=141dCr)DQ435?Xtd^x015KhCsL7^XJdYj^^0` zABz(%vJI>ocfcR5YHvU;V)6X#WQa;~hZMxc#y$nuFzI08Ot6N6%Li?RhyxMV3$)RX zp3bOX@3GC!sI;lW9kh^^UiYec*2-X2oZ|V_t5<<;vJmAkl(bKUf{@blCY%G0yC`;r zy}d;%!)z0Y4b=B&eY0b<*W?B!r+`mIryw5wT4g#BwsNDJ08l_?^K?q|FU3~{lkEUG zn}OP|!6bJ>IL0vPw^~MGp@^$_D%q8}B7~?f)e)%1eie29u-xZp9MT2RT^o6?#Gf^z!ee~G!v4(ixe~bl4t8F8kl<=y@$HzCuOM>TE zo@1#;Yfmg^Z}gWDD&vwa(k)8%KApd(gmobv&ebBbX@X-=6(yig8vGk8m>-ilLmIR= z^2!F?;2r+#&!7ZjpfQdr;vFraT*3i5vQdjpzgw;V+#Dfz!EQp4HQ804TIVZq_gYJK zj?aODyBmyKj6HWDcuSY9@oS9$AYcTcngy-!C0>VDH4AjQKvlra?k9dB#t?X@+RW5X zkxypl6s-AS7g*qR2P{Y$ya(z9uaS`41?4}4A_fZ^D%eLuFoHc;p3`r%f+3Yt@Ytj>b#b-9;3Afk43geL+RjZU3; z-hx{<{7WMse~x@BNLh_7x^Q@79~-$J75P|HG%vP)X8RIlZxU&>v<%$I0r0H zJ!>zQl$7Y~_4)EzsnxzV+612o{wEI_GydZ9%|0+(z;Cb6`+s zPuF==}WefafKOu-A!ks|hk zU&e@&EShq6Lv**i1OAtstA1mFP|zX1Kx9Bd{ma+r+O9FA0RWO}|IEw4Y~gU=o(c$Y znxSzOz^Jwgs&~svo|VHB_v$?6TPUT!$TghGH#65z`)ihJDacaCS7rP zA(o|zb7vyQx39O?d_b0w2eOZfGR-H=9QP$7A<0nBQs!LWh@$HthzznS&<(yyN<@?b zZZw_HhxKh*y&^A~=Cl1L(K-o9`GHAw9&z^E0M0P)Xqa2VxPQ98v$J3yExx9`X>~q~ zkoSg<JnoYO7Q{IqOA;)Pdl4+|KPr(K;jGq5lAf_khQ1tk5xzW!ARg z{K*f)zI6HRjp-69Z#g0n*XrICv%bu8#fo4-P^4}V=bOGOsUNqXN{?MfPJ7|bmNUJk zOnQIE_U{VdD~e=KIR^7I4myhi2yM zv{(N}!G0qW7^gpc@W5y!MOw0cmwn4oPI>7yLq z3bG7{#4-qSz;z@MGJ3^`Y*q$h{~ogd#%yEpo_`8a{0l#xb$yD0aD#ad{OzjtMmE7L z7Is~oUy1vi<(+3O0XDvs!Mk6*9P}Luj`zjwoC#UfYoaG}gna!|AFXi_ zd`E&_%m`!O>H9Iv8Pm%-t=2Z3dk$aoQ0pX5Uv|p1bDu1{EYkHc(-{64T@b zDx^_13Nxef&=_VgGgyMZhUv_wG++y%=QG}SK{s~ z_kUMqiT3~A8T9mRCaJQJ#bx0N!}x;w$@x*zS>9Uf`X)(_HccsMX|eM4hqGya!TVb%+*y=Dz*H(mRF;o_kHTn&WjY_vkX=&YBm+k_TpG2^#y-)zPz zL&ZD~s__p*G0D|i`%pApWd&h8s?vY8i(Gp>{Iv7pA z*Kp*dn9(ebSIZA6XGswP&=T;$hGu?zj~{=8&zuJX<6L20e=&ku_|`*Sq>*L9qT+KYE z?$9-MFm^yh&iPdM_+`z$8IUcef_7j4B}j&%dw0f62coKbMd_a$^B$MYfheKP2QZ`x z!RCjUtan*ERcEXiDzEY#F{Jx-^HurlA}^f`WbLV_VZ`Y~p3T_uWOHyXv5$exao60W zxf!m#%CVsiH$xVry7`&8BNi&Tij<;dr028%C8g#TS~1-Yc2jv;h_mRJgeF%{|l z$JhdB-hsdML@a@Iax*W$6ZJ`)PsXlVkqKarE)Ji+>}tY$Ij&nGYu&F64h?nm_FjJd z1*`3lkDCXl8t9kkVG0$UsQr4uzAK+&W9O*XG~hkxi%DqkqhKsRgY6R+y1@HLFd%?x zJ^ZuqgO%0FbD$=(+qt22P&V^*`|{YbKpwlLH;4hlufC5qAR7U?YciZ^ z&n?C%96Jr@gWSL`AY^&rzPiPUG)a3N+1@A7QL2^6``}T|Amk~0Tt`jq=VHfpl+9A- zep$|`gSbjBMyuR+ZDIK#(W;k{mWy3(^|I&wS`RSs+;lnx-Q)SY9%{L3*RIEd^VHnZ zLoRpXPUO4OL?g1CEig=XM(T3UOy2VyJMEo9^LQ-)=Xvc2yt_2mup*g)}7711ka++ai6 z{qvs8rkN|ZPYKL`I?n^A+WMWtGBS%i(io2;llS^N%N&0|5JZpYINTd<@+!-T3f8r> ze6ZD%ytVC-w|UYE4!p1|`%(Sw44VtJ*x%V-$wTufkybqofI$o%*ct5mWs6-ul!bvk zwuQ>jXj){en&kZkJ2)tT-E*cNkq6EI7Htwefc*2_$?$zHI1h+}9UE{76$&+oR5LoK z)r7bzu&B5SUsQOj$y3#v|45e*HUjFJn$H0wmk#sq1qmco?8E`4Xg>1EbR$rjaTSEs zPUe38#%2vuq#->p*92lS5IXa2*!$-4Ho6xHy|TyOw{b@^m$bHGk9MB>>=iF8|Km9Z z(=UV#bG+4XAZVTZR~e$moHVVe@#!1nJexC#*SW`>4;+e6rdILau0=ilze*z0>z(Hc$# goo)12kf1v8{k&r2;B-f!76<&uKU0>;mof_YAH5`@5dZ)H literal 0 HcmV?d00001 diff --git a/docs/images/re_use_log/logAnalyticsList.png b/docs/images/re_use_log/logAnalyticsList.png new file mode 100644 index 0000000000000000000000000000000000000000..6dcf4640bc03f2808f206adde737acfeb144e606 GIT binary patch literal 90986 zcmeFZcT`hf+cs#!f)pD?5Kxicq<0VzkS;Y4nt=2Yq<2&VR6rD@H|atMC=ddHP((na zmn8IF6CkuuLdhJSc^`kzynlRa*1P7LHGj-mE9I1&v&+5j`?~IZ?Yw`cqee~1M0w`S z8EW;XPxQ~6x#WN5%z3fP7l9+D0^4K2$5}6ZwZ~`5dhV=@BK*_VMSR z>-oyu(T#`5r07gh*^x}(O|&<|Yn7_y;RT=w|FzZN#UPXBA3uJqKp>JI6dYgu_lYxS zYVg-L&i&ne6?=F3wCMk}F)KCw$E6Z)UNQXp%$YN;7tRX(vp0PCoaH}z->+Y&|7Y(x z`(^*Le|MWyul?VC;4ICh`p-C78AD$$_HGJeOb>P9#IgrWoMBwK+HI^Pz-uXm>-^t4 zKPr^HvvFVfm_)n0k)xb;+`{j5{qM;$PcPK_#o4rea2hhR_1^h?z5B#E323F>e=0w1 z_3_o7;Oy*d{Ozq!#ci|4|4}1jSGj5J{(|#dXurS|^R~Bt+`Yf8wnY?g#udTIB>|Sv zo1wP7POA*${BA`~!`S4k!2(0|VqtK@&L>_}WjO zU$9jkuoXkXmf{f(*OOW*S zmU^}t8fxKrJM&JUwZO{ilX4@D<03D&HJq`b2C6Ww+M?lvG{lYCTBBQQERPZVs6noS zmEh~h=)MZ1FXad$j38j&sHfA@!zAro)srQw`THM(e1vp4H+j4?h?MS`*<23YzIuZ= zwp~Uap7`f^y+U)Z>vPqj*Fs6A$`Z z3Q}VTvfRfyQFlYP0f1h{e#BTlLD}?8jkUh5EJ8Wdo$kCfy7WJIPZ}WK5q6g6Y1n!PL6|bw7hgBHl z$jhGg(VCR!Pd$goMBY3*X*mT*iW|=S?)xiO(&U_SdB2o@U5cVg%(UnsF#ml-Q=_uN z{qDMiXjK023zf9111gp}i7$%MOu^*I{2g)QVlan%|ARxdD2>9TCW>2ZY~>vZoF$$M zf8N&py~i1m5yvL8#{ztsGMQhg7wM7l`A^)Wg0(Azc*`@=_9wr#p+9J7U?Ml3c7Fr==Qk~M`dO|`Uhd2yY3XY4!cRv~8n&U~m-Fr!{1vcjy ze7+}REyi7au|4NKgsM5gEx3n{bn{uLXCF#yT)7hHJ)jskM9xnl$id=5 z6IHbnFWZpEQIYEgnMj{2 ze*bvS`QJScsaoXz!>@TPiT~TT>$*$?YL3?Y<3!F-viceeRR~9JnPNFx`jECdNoU9*uxi{H)GuM=9swi6Z5=Yw7Kiy5) zZEj7;AY&@{<-Zf)^nEW3Ey7Poh-gzxnCu7w@dCiMf|sv@Mf^QR}9I= zt#Gr}hhcqEGdp+$Wa%D1pI;(h=+ptVT&(?DzgLfyn_OIG-&FDT&DNHeAMs~pncwEV zKgmo7)LHzCANDnqLO^l#otS>RZGO)6%*oH>vh$RR^=iyTJ_&W<0W^Q_^mXIf#%Rhf z;tA!`+LLjpwiUe8L0I|MIe|&*%{zau`u;LIc-IGk)yCb9a5X< zA<8iF;GCt|joV+1l3-rxzJu&^1tpot4>J?GSymal_vdyb=g<@4HLQEMacIsVn&G zcb#MZW9>{4)0RkTtXKHO)M&Q>)a{MJ2RM(7b0J6iYzJ^B&fZZUQMO4pnG&q#Alx5P zb)&nyQ`@HMXOfO_V;OA0LNX-4C%vNxdpU&R*5`)OyVQt%vn;rpV_tx?IPi zHG5NsRl7(`DS?R0qMFQcOUJC0$=_aq1h8Hf0lTJOxwl>IU%n3HncE&J}+<Zjq*ISxa zQbAi04}gdl1I}f}9*^x6N`$?AIJ*;N~AV$~CVp`jS$5m`&=Q zcYu>SS_Hl&yq{4bxpzJC*>&L*ie3m1?_3I%5C&UoR+bfY?Ipw!%n>Djf#JH$@g*wm z|LWY-4Ept5wuepTu9fSxuO#i;`Q-^?&oU2DMMCG4y+sCOLf*fRdG>S~#57R}udiz; zdc}WF(^$B!2nw3R;3S-r0P{E2QpFE{VN_Y$#r|57{j+yaAQP-<)*b28@$+Hy(qv%0 zZS4*@Cym8FCy zndW<0GLr5v_i`P~toDpdK)-VVH|PD3pq24w3d#;V`Cpl}G-?{~Xt2=nwbF!@=#SF3 z3odL2*M8`4`&{mrwSe^5L;pG$1bXluCvM|S&eo>R*vWZchiw+Mx7^d)dt!qXGE0j# zpW6J~l6=SsKowRF*$4`QUY0_LTpiVm&cWEoEu5U;O$G|Q$CDWJ3FoFwTc~e*wTTwu z)=e?#wIH@cA9=fm>>la`(Qzw1=`}Nm9qQjBeLQiCd8;Q~76goQOgg*Tq`^$tI{ek% z25rdvphJbk;`JT7r*buqgbsFx{AL#8@ir1MdPPNz`cGM%oDBOIiRsTrkd>d-Xoqlf z^u2?14n5D;3r+DybkC;gUGzq!JYT6^Qc1sB`b2=KB$O*btq&bGV;xHkr=p}@S)_0)Vuw?pqzX_Am2D8{XqLthl zJ=%E2XGII;F{@tsq!4+*DNovU>`t!h63x{glza7c`vq{R-dlOGwlcp7XbPE(URrv% zbw!|0PY&9(`Uc%@$209Jsx(N`OzL^!kw)}dXR~JBQW$hHE6Gbe%3)V;>7|nNrFaUT zdrw>HXwdcMyL$0oYUcQeOj3M@=~|SvI&Bvki+UQ$ohO@*1cYUy!j{>ymmC)GL2+Ab zIqvM0b;a|>*ndO)ril{rtFwV#^G~%?caf^Jyf*S8I&`H|Pf9qt^C_I{C!N|mr|K2t! z?I#l#dRTp@qx6N%Vum2%y z1e?Ow4f!#;jpOJoQ32P^c_GO2uIEBTws|*kju#Vc8Fdq}SBFY`=zF1iMi4gjt!L!g zbh>)G$~Or9p-vzE#m_?lU{zDn919!%_7*#b`4sh?6!CoMa^7U>*b=xqd-LPP!xvS5 zs;|jew7`D0dTs?Rm{F{1Sd~Z7a=RYdQ*?!?g^&50K&B4n7nWSm_et}M&!KAq%EN80 zZ>rzjJ*KnHa?HYN##Q||agB1I8QLzq+h2@~yVxkJyp46&G2<%D|^^VlZk z^>x;*tu-jN(HuHmAoQnG#QZ(DB#!Udv}6k3HZFx z^@VxMcD3Vne#2?5%+jS;${~Vu)dz29Qf`v_RNgD0sN%-BqH)b4HNGmYB?@u@rSkOiOHSwU_VVoH z)(oqmljD?;c6nP&QJhe3W%})twwIW&*Wr#~r(lS9;E8<%f3RL`v}LxeYl6==u+ML; z5XCMKTwpTVVCA%Wpgf1(hj7;(E`mB~QR5!Ek2LsL1_-;agEAc4zb zO3k&IF*R|o--q3jRxYF+ zT`}L)u{~G7)HtTK&Fda*^Ox_`?V&+ayGCnwDx?sqEjtUB@xm@G^!(x!?DxQOSeYHS zgGnuvIoDwX(kG?xpwNy!ZS6JK?%+TRFeMMdu(t7ah(cQde6gT^Fseh=#g_x&E`+m# zYVkjginF`a<~ao|^|1D9)zcX76cVELL{3;xGkxUe(lmP`P14PZ z+fkCdCd_+9}KPNyvi- z50*doWDdbQ*)^ViXH}9B6EhS@lN|glIrTm5-&BBnQCD$7&9=P(wlb*L-vwzz5C4Eb zmu~8&IMfx5pFdkJT@MVY0QJcciu?2>yDa%VNjJMDx*{*RMB9Zl$=$Z7 zqL=YzsxBz+5kWp(9o#sg+~^syRWaQ?E$;|E?0IuV*8_` ztcTiB-7d5WtsWPg7$PTHjz9)eGv+0~m-}$nDGWQuOsxhiFYH}3_1Q~TyiRhT?yFk= zEGouWe2@UEtEiq|UvaXamr@t%rZP5c&|h8+pl14V*idS-v{Tk5`os~GvcefPZkf$P zkhy(h83JeX#EXh7c}SSOr`$l4zO%$(MG^A$(i5O|&)7(wohG6K0{RzVyGB_vE~@Y4 z2i={H?dz~j>Xk=N%Go>knxoLeB|(D^BUr2anOMtO=yez7U~K^u9IRO-ZdtyS0Mq=# z+t^bUv`A%I=R{-X=$@Jp{b9W}(v=l{^~>T@4MQ!%n(ET4hD~o)0BZi0=zXip7b zneBSX-$ltjSm=oN;2n7L6z}ELar9nwYwc5X^Qw!w2Qq}bQoC|t%K#qox^fiXMUwJJ=ZhqaipI7q!lS6*Y;S5VNRuK^n zkYLHqFPAf`kc1|XK)R_yuhMx4-Y5A+VGF&0Or=p#GBO=0++&ve)4suZ0_NLlNeb0r zReCw76gYx|sJP6{zus~h)|#+PD{BqP+s4jHC8VeMFQEtOGguv8$oDQV;xU;R_bMG# z$0grRff427AUbb3oCizw=us=9#xez%U>o}X=VLbR>iG=B?=l!`6r0MWIao1rF)W**+ zV)G?}OY7Fc;>bGB-jk-FVUd)?q7vhZ;H;ap3rV0XIaYV#I4rl_?J9);N7)|el~Hun zwOB}k_~j1&{TNZ|jDT^gMiXCX;LVIXTZoG{;x4VsB?A=0T{!@;S`u2`P?FphQqUTG z1YdamR>zXRYr0iam-vI~@f*1Cu;)QPX=19Pe7XTQ#S@mD_wX&-_m5clk|ac6mQYzk znpD`Eqb}ITo9tC~{V(LFYIEw5AN%TABuDIEb=gQWdtc+ZjY!A2$Q?5Bpd{qHIyz)X zLDa0uoX|fC@5nvz#=T(aQhj1FB{wsZQ&LM&lq$E7D!mR-2ne7%DvS;vB*vrnn-tQP z6_rAz#V5MwbDF&t9lzHy?|os6pa?DKmpAK#c0bZKXZYOv6|2lzJMr>!*^>^rDU*9c z9Uc+jH8r_;>q*w_TXvMYkb$)l21F_fh5D1+8>*;RY9dU>XWS)c8gJMT956CAbzbpw z(&==2b|#u$VCYLn!f786*&aSpw(?gx%`;qLt1nlHrcZNdbQli@g=fxtU)_?8(>Qnv zrnIOs7N}ScMNZ!rgS_Y2K}FCAG4W1)R3@rCud%7X^P)SnjpXI#jo(e_q1{oXZTF~6 z9E2rYhE!qIzx|*z-2PWbf6iOV6k)Id8>n8Y6*yBP!mCIQtW_sd{4HQK)38I+ z(ikmo!lrwU+u=h4cjPqmUwm%}0+I@cT>~@qBIw7SF1Y+UjU-`R zK5nks0RL~!XIU4tC!S7a1^zqHRl}UxGC>8f^uF)bE#h`t^Fb2xs7vQ8B~2xtUmdiD z3Z{GA$Z0W-_3rNcLGRmyRDo2_+H8%82xZ?tUp0I`qh3##?-;(|`<6MV$YmqtE8ccm zrlteh{xqc42K5Hv3L2g{Pf0sb7R!vqCkSGMP&{-&PVW}B&Yh?GG`S_l>rt$-*TssP zltu_ELe3g2-|~IHHm0YO@u%2s%87EgQmvVf)6s)jw>I@B?wd+^389Ob%xn}A3~Q^U z9!aDh&7z|DvszXg1e)sd?0gwyD{-N~=Mw9U{Mgj7^thv7Exu;7Hitkq8x_dEp}N;2 z!VHe_>lAT!&Ef9X&hBF45sLOj&&VILx57KwJU#uaRCR$`Q{nKh}E3%mA zQX2cDXsxX=uwR-;Q|~FWcRm9n&Sss2P)vLP8@@PssT|uDdk_{pC7CV=g(&~Sd@UBc zGod^QT=?$K?698`RC|HHgJ>_*8`;SlTGxcqeyq;;gsOR1-~d{wpSKfEaxv01Aa~0? z-eT>P;Q!@ZicR+!2rDifoirvCY>L`1ch<8Gk}TPnjpA*N=m5X6Wqif-N0W0Gre_e- z88&*?$9?*qpP!@`#`ImrP%XsDqW9xDVkhZ|dOxXk%7$8kCI5t2rify}Nmj8I?QFD7 z)Jczp}(AS>{Fi*5UEN~YL+Q>bi49rm?T?_ zYE`RXLP&<(v4p;i!XK0r)Na<*6*-$xgeVycgnCse{vxSTNa{{FSo+?3xpgOBSJh;S z>Y7Brt4Ij<(+Btm2$=>ZIk%k0zA$XEt{K6==BROC*1V3N#$fsEwpisgw<{IF>xH9P zSVYuMXaaG;O(F_yW=RrnjQPuYEo}cg$(1r7pFO{``1^C3NI(V9_AY|e zPJ54x=+gWI=+=5^;GmKG4iNldsirQXhJ2nf_g0&stLUq-HGh;?M&btvbp&tYfw% z4su=VA`mZaUc541t5JY$H(4T80-k>yXAU%jze9f%B3i$G{8R4BE@m3iUR<|O_2nUp zg1dCi-TI-1@YcO*#V7YZn^g(hj_qw4X?7Y{t}ASHQgw&Jg{8pvZd-_JZs8_7NZXq6 z^$XbMt$>)tZenlcx~r)pi!ss%{{k)UXAPBq@v(tmefx&03o-gUaUxdig3s2or7NO4 zNHpWmfJ+%r&|-t7&5?ss)&dnx!&av`PlPE~=gR9s49r1UA=?`@wjSJpj6rL9k%J{m z`|$ss6q+!d7E24{c_qXc;}b~?wzl#nsGbdFUOq=uksI>CeruOWbV{JEZ6Ad4dKbpZ5b)Fe&5s$juoRX-amXNXiMc4YZ+4Di<>+t`DY)zIg}Q3b^USP)()bZc zRLF|W;OTmfaRU~6nDmJNw>u_Q>?(|S7xQ&MrlT>1_j9j?JQ_NH9u#;X@(>RoT2?Y$a6iYHniL{wT{0>b^2XK9sS20>^EANpUis5 z;UA%P;X+86P9QO$Kkx|09VV~nfqYzHQJ4?JIxVZ`Xy|K@t^wg(KrkUb|Cgg%#>2=FL&ZYN&_=6DXPtVeU0F6C2EC*Fjd=;}@Tt{-W}uzT z0^+%bDJqv)Vjq7g((UEBcuihW{%KSmP)7gPs$rIWNqq}LhZguv^TZp4LL{*S-9}-O zyWOXs_j5%N1^vh1&bnUEs86@e$G=Eb$GsPYemvDdQX*`mlv#e z-I_8*!BC-r{nqnZ{W~%%FzY0E!zcWUR!b%bt{NQFs4tX_!AaUD8DEN6d0|+UOLNhM z2uunSI%1SkAdp%piLVcSdwdWrZr<^!0g)T{F2Ymuj9p)fX#tr zBA=L)*Z4T2TR?dnsk=J|Zl=RTWzh7K!DuXlpb>9V_3{aWe4_}Pt+tH6)6OUZ-ROZ) z^s}7w21PmTP-D?WWsUD`lFW{1qB9y~>^S#5IA19>7hYrK5?j4$j2sk;2pJLZ8t+aJ z4f+GAP`Qd`o#G8nv%_nF9n@|=yEbq0t%XpMkf4DG!u0{v_My~micV8*|z=cX_cSQ>0c4i0e zm?fs03OdZaLk5Nx1bnC48LT>wDs#Jc|914&VWwRrt=1K+9}4L_YuWieK$t)vJo$?%rh6PG z>uc|@u-HMeF}{0cuFXNV<3n4SbfZ$m>Eo4GLMuFh#G>e}da)u8(27M>*!ps&WJ95ECtS<=eVaDqK0_FS^FQI>kIwj=4|V%%mA)u+mZ5zzJ$ zmHu&Xh#JL0evf=zDi?hzv9kAJh>iHV^Na`np_0-G;dgm%?E_gIUeh5t0XANe^Gk-mvN807IbUFy{KN9@{*)a)_2o;CpULIxggMm zTB9A~C6K-2Tp65cZH`mrE<03#6~DppZVF;tIDwjo)%%lCi-XxYWZ&0fM{M+7dnQ zaM+uRh|MY!kd7yGF0VPk7=V_t&Z*ioMvlQj#f?q}li?&gHZA*3?a)C9$GI3KXVan# zl}{-rw~-h`7V6At;%_f~*KzL1@3as`0&dqsq-#1RTFf%7Qty%l#>5-CiT4|?q%SDi z<_97+4bdMkY(22tsCrrIfn@@gmw(xe+TBo!4?BTK@L`yWKN?Lbdcj?k)UYT%uD-VY z+pnOicTL_k=jU!IdXGJ**p7v^)|B&&`+^}{j|+9Qe#dRR(sYV;vwB_PSi%$kPelYaD&H=5Yi{dft4W_rRGqYX* zqdmGi95jz`I%J#5z-j=-4$LVW!rttaVvqa=cPmFi9e}p+$I2FlJd=s?#GQ25N5II& zdD-{}kU>&BkjRA1<*k?3=N+64x>aG5Qm;KxH1+;3hiRIa7F^CfV6B*w57_&0*`X4T zqeB=ct;1wj668gPvXLNIEMIO9Mzqh6ly2# z`vd81;PBJp|D=w<`#2!ayQ&$}s6giZ>8!u>@n9v79!O+sZ8uO?r%V@~gvi>loWJ$= z^6z>V>T4+n+LaxgG!1iWo51;vk$x;WuWm&5@gM7@p zDGcr0QR$GlP3}U86n-h6d38E%Ee6_GyX-M&F((i8?eC(aSarrg<#_`qls1rq>@e zU42oK&wBGB^mTy9zYm}BNCq0L)X)IFwdnNsVAI6w|F!C~X`mQ38rbt6lB610K;fC% zhG<_Ie>Olcj(PRf4Y~gE`%yl>rB`D+v>0C&Yu;!Tl_eJGELemH_%W$ z$g)gBe4GF8&zo4(ny#8?u!SS^6EzlZ{5}4!+KRZL9pOlm6zA;zb8GG0C{Vw z?M7&ksKwM^7YLjZ{U1+!^DmHa|L@gjrh8oeBlaZI|1~PW3jW6`*8g2r@K3S-yD|T- zd+eXz{XeDC{r_wDw@v@&O=|7H7-^m5F9~L4%Hvwf9;yg6B0dR`^b%fwTpW2T_`Xi# zo%-BQgI6dUJ+!$5IaY9RL+HT=Iyi`^uVM(*(i1P6E$!K(OV$+!jf|OgUp#`E*Cv%@ zpEQ&%9NO$Cm-CjG{M3v0=9Dn)6_PE;B2Mgdg>x9ybMLK8PuY!i`1_>!m?z)VFD*nA z&y>J>jX_tq@G{p!#8txX)RpM$(G@j}+v>DFC?jre4VJz1 zEVvmVD9B^Pj?FZx-6QMa%j0mz)Ow_YY?C#`WNt~WAb8n2?^IgrP<=@>Z=E!+0*{gP z(hcIa;*UT`$QN)}h~dH{Sq(yw(S76M`90*uHdgHUCQo7znT?t(tsll3J;;^b)A?{f zkVbdP+cNA8cc6xq_m~ zxA8;ra%-YP#f6qL9&kJ#XF5Pe1%a4=*0~ zJoLRjHIImdX_0MwmHd6nBHIKy|K$(WnK8*~9ru~~t4l3iEHFnR(^f; zKc9*8Vmly~TCT`OWo=>pIahCmN~K#fpL)9x$I3BLFAasyEvcdF&vhvIQv{xzdAROg zH5~zy^5ZZK6^$!)ugwMTWS8*bo^!Q)nw+{;(2Fqdi@Ybk^N50LyYp4L>ebVkoB0LB ztL!_2Q^_3fS1D>bQ_VJRY~=O@-wsw1Dwkmgo+u7;Q6X;dT4xnJN{VV*CS#u9hFc$m z3|3Y|N(i}SQPeC2H!Qpf9e%0Ts9>dRXU^g+(Aqbsm^D-$Rjv`wT4uq&J-ZQ6b4&i{ zSXdrgRb}a9+E&j!oppMdCSR{(Pnl+5wK|VB9;_r(CYP6xoZw02FZwn^r%&`M`>mJK z>XligrIXZ}X%4Tc?*Y~Oy$n7X%StTWm2KvlMF(Jnk2ehIdLSN};{s<1m}vo-jo#4h z7koWEJ%Rc7JWsDV1Q21SeM!AP-?^3-n_O&^ykC@4b!J@3t4VY1an6DNU_hdePhS!s zQ}M3hQ`a5x8w9==KYpnef;_0NWt6xtD5ygYnW2bf-uqa)9NC>JY8K~5C|q84^wOhO zi(@rHw6mgtgnnYY=bm3TFu~8IVyTYlk9L|61hmeBlJa~my9P#LhsC_4LWhN}AEb|# z^@YFrI;u#u(YGUWbQKOvELc}c&tIZRiPn1*c~AS89qi?6F)+q5PV|Y>;9bdpJG{&A z2l-+a$r!@~-kv}w^ldlk>ke59WzQKS&z(O%wD7sdd0^P=Dt(t^uczh{H8uR^`h=K6 zkE_@9SLxH>AxjmORc~I-5$8Z}^9p^c4){}yLB(8heANb_GYz#JzniU@&+IT<3Trm= z-e^+fHLdtkVz~KxS1s5;}FL3Z~2or&FOnOh;+G`qgY!0x`u-*|?o zKMa0`<(I~X@Mk3xh1OG~Lm)45%Pd9bpF@h2`?06Lz-=4+(vC1b+#`U36s-=b9LpL{ zNS)>xD*H1nf%`ch@5IdC6&E*B$HsRhybKSO_E#Z5-svk6;+YmeIyAc!gyi~CXt=$i zkqTh`V*VpeDs7YdCo^%1Z$!d$MOeGj#nGZ6Sr`%#?~|9A?>dGwnrn|Wm_Y8~C;X-i z;Lc7KRdYQ|lIxlLQ+{pX&yLz!TU(trXH^0Vdk38-OPv;F+{UV76%O|2x(@UW4B(;1 zB-gmkaD=NK&>JUvI6mCunsBC!&mSDb1nIf(;iCLr>FKM?yw*mrfdiMs2l(Z(w2h!I z#)LQ{&yt+XqFG{ESpKO`8|1~-#Yo0f-P6T|dOz*ukLd&a;mYfkDDl;$rxu#EKSDHo zZ``~|>_qw)<139c*{E+H)mKBN3WtUaw>4j#>%8m-_4i-?u^tDVYsst#F)W+44t2_@ z^*E`gKdyQd&2Zx1*>hTI_kBq=U?92^r|9iYBb?-sKNMAGUQwG0zx$C*w$QIV4%E~4Km{u+>nVZQoN9H8*fp9+~t}fos_ww*Ky}k2<(Ag3*L|H8(#kKU5 z5?V>smDiL#X`=-;_L{!xq#tonC&4-P4Ry!GrpRv%E2gHW*rINYp}Uo0ceS$DY3oZd zFui*P*PmA;Xo1YvW1p&@NkY7CyjT)^uodXi#VC(~rH@v-Ib>1zJxvv{JC(kq$=Q{l z^DJbowoCmohlfp2QlRhOa6?lw27yLA#}qG`DkwZ721$ zv|_RXOAwPxqN)~1Z@ni^-dYB3ssL3B`Vd{W$=r6P(*uhwS5BJSn7CIb=C{k=ohh#w zMZ@~c*}3G#>>w`KS|)Jk-Rc_4m$bB!cL`iwtr58|I`ji`K@j;-SY1ZOx_Ph6CEBCB z>|+p*gaqakFg-VjPe>4-tL)twGC);14Q4xIs+<;yM>0M*=fOg4QgGXnSW`=*EYCb8)XC`EfM8Q2%eeyd4vSJ88^-Tty&~9%_^`nQ{Ku| zpJLbYK*NF7mwWi}VI)|wCUQ}6Ek~bzq9^OUHfF1jQGhnK|8_JcvLJ&+jO;`UV>4G? zYQvyUe*k4o-05uQlkvO)oQ=+aEx8)&ri(gLL`*6*uZ?-&#JlvBstrx zN{h>hV%1MBvmjzpgikdO^~{o(ExS(dnc(I~#u^|j^r&TnT0bPZo9(|n^78R98uMM- z=~JdPm3PLJI%N^d5>isWvB>&z-@6yqZcmZf`1SGL+TeHW36}$dgTVeKS9hu`h#a&g zemY<#{pYl`50}|vS&x$*I4$*Lf-4stP$V2#%x^OIc&|~Y1V90&{$sH#6IlH<`ZPW` zICy^D(lYGm`j#HBiaH^vM#p^s7J?ae1XWgvI+y$oFy82+Pn{ohk+oj0_&{aL6MpUZU+?Eg`w1&x zq-$+t;|Ve=xFUHn)FA^?YEcBTI?l$F9++l_%E-49FVAz919?w5c^7C>Q4tW;pZ&0D z^%b+2&& zQ1QVE9>%aUK42huw7;?Ujaep4T}KBqubyzaV3qo|h@oP+DU_B=rvjNpAaD0NkCYS~ z6znGpQz>gE@nZ;$WOupF};nXhCTG!>|iREVjI`a7-P!GLO+h}d2t0y;nZ>xWSxTNQy}rRE=o@j0VT_dW&*tNCp37tX2-`gViL3UC0gF!UKXD=$sX&&mkX zH{xRHi{|TH{sXT%pFgXzhHkD*8ILaTnK*j3HtWe_Q;t4_toIx*3V?sNwyJa{33P5= za^GIm)=*d11HyEd%J=KQqdkSJEXhGcqqT|3cJmCrmWMknrKV|$VOb@H#WsS1hLQIU zfM^{%pdyOz?B)gh<7;h=mEl$w65fBNTAvT_n_z@H4Co`=YOUAetT_i4*HpzL>Ke0O z0D5SPxy@@-@9QI@)1o1@xir3k-*YQJNDUWTb1ZdD1!)3|)3B{osg?gv2FsdJbxQbb zyndqj2AB`jJ0m?gjF)=V(j9=AL?P9{L{O?sBR#ipYWv|Rc z$&{$uLgbI0sdcApj=141VMc*XE>B4zPpQ`ex((PA!FtE_njP?WnGKg4zWQO+><;&x z@f37?&P6Aa1!BiPK8(tiR!dM7f39`SF2TTxLQokzoI_<4rg9a>irNY|l{055+ zuBS=*xi9Xa*M6uGm$Jj=84K-x`&-HStq-XU78V0|5V|&fbg+G@DnnPhrFSg|TGj)4 z)2ia)VgM>Uf(jFm^1I(8O0CLIK-2-U**4f-zR9b{|@3H25Bp;VACc;HG$A=Jlcvz;~9i3z0JVC z+c^uJi6ptV>b9Q`vT2@hehav9x@M7T+26$mcX}JtRNnNNt-H zrQ+Ftl95}+=`zy5*vdJ%zqGNj@hn}!4Gu)^(?C^g!qAhA9VpOTz~hT>;w9-LJ)bB?(dcd4YXfZ?H_0k%EL`|B-*|UrHf#I()Q72yr2yr?e#&pTBvABgO2_ueM`}5S$wSz6B+2~%FrPunn zG||7lE)unyR%TH*sd2bEQVD?kAN<=(tvx+D@_{>jQ1W56WzZTtII8~k%=*?s=gx?O zsA1>epqyq7k}*4%<@%eFwTrL9u|p*Vx=F{c zIJQrOihg8yjp~SmV%hKC-x(>&21WqEyj_P4;6HLpiU2YMus^?7kUL(~qq3Hi8wi)6-vl*MjNjd~}+dn-^^&A|h6Xf7s#% z3S6oguB=~#mDD*yUcB+mLN-{*c%P(6utX1@r_0!$pbPN@C{nH!29}VVoD4V|H^H7S zn`6`S^ZANoEqqS%Rz|z06#y7+5<^QsNp8Y(;sI;PlyfOv52@Bh;hi{1Vk^1fYa{vO zUm1aybCVqzl0{Q5I(r?mvak-nzYSJ8kiRxHo$(3CuN*x*z@b4_JbjxxsJo3b?!I^K z+!;c;nN5I-%5<`23c_}yQXP1{w?xqvoBWvNwQ6|73)=i-`}sblV(_qBy1u@Cl~=p3 zfaQMPVoz2*o|Q(g$G^^F6*nN;GMxYU3)12yaARvFJoU>@h9=# zE-t3LI#E;*&EO1Nk*4TZjUm&2+h~c}nLDZ-Wvb`!ngp1CmH$i}5}<7WD8T4mU9>yN z48P<_u)_@%Up|%!E2BB8w7PQT3b-~n6(~UBL8!=Ndj&_dIHZR*O*SYgNx-z!c4XB+ z$O8F|*m%*oxnBBThxx(ZlrPb8KYX4^FLb0QEQ}4C@-zd`L{qh^3_TZO^AIj4#_2U0 z7b5pUt2C@7?hREz;yqzuz0M>-J)n(={&B1aG!vdO3_Viry##6E$?+k2vR6`c?8gg* z)|-ghclGsH+;hhEfFi9O0qIwu%MLftlOH*Q_j6S1HzRej z7yY4rU4{TJ~?TB)T*P-EPCuI!)5(~iei=Vu|0j!temOote z@_hNSpKKYVolvBO{dUJ#n=oq1uGH|!VD0UolWGc>rFII51?9{UEB%0 zfiNFa`Dgdma>tTxzAVA7=ZlmbG)g^p>dk62%6Uv74p2Jt7pMM4J6+t!a7da();`2~ z3u|?rNCa5{wFzLo(I_4u*nvC!?cN7lc7Qz%9u?7D+lbakpK;uN{j zDmrfFMg*=DBm&Ac*&zav6Ka*R8^LXFS>)bUE$~0 zo|sltRvIAIC+L`&p+J^CH5wk$PLnAcWE=+oxwQt7@{qKGM!;>RLt;LamzOJKNU6uk z4X898RpSM7}YoNZU=#;`5s+r^kDWTiwv?KMn>987#XISEVxkP{w3~MW#Lbv5yIU9fUj8R5El7B!_S{T%lL16&CM-C zP|5qQrr6lpqLDs5469x?Gy%$wJr&P%FR#q{M%2Z7-&0U=ut`97&9%AjcBu0>1fd1Z z3@_!~tQ>#%QnV*SI?o4>`BV1;!E3B_wS21*26tJ4?kVm+%F4>x+SwU8WDEZKa&F2f z@lRrb`R?(7^u>o%%77J=-3FAYGdh`KF9%+rPQuu~qBa*i;Hl6?Y@We|`O5h+s3i0K=%ZU6Q`jSjVUaw+hbtjfKm zl|RWS{5XexqA(j^?%~;N`K_At(2yvaQ!@k749378_ArTWX?Lf~Vx6rLCw#ZMM0*`q zhY?1FTDN`HNZ$alzBAk{Rh~FxP}|dJ-4aNBN9xhc%uMRocGK(gaED%2>d??n=)+!} zr@sXgd5yrd3j1Gy9Ly=gO(@hB4;_~RAYy8nCCWLA!{D@EDvbpIzMO3=`4g~E?XZ(# z00Tg+70BzEeZ6)0X@)QL5Yk*jwv$(m9jxDiLw%mKei+4TPgN|ATz$aHt5KwrsSm&% zFVv=9JU|ul)5KkP#l(hi2vG@6)wy<5UcL)*wdqfjcZKZ=hpvluk9ZNr6Mxp}H~Kc2$3(9zM^Wyw!bZry$WpsDm*^&BRe$pTmc zbNK(k+*e0MwZ8v)^q8m|6+}QlK}1@RP89(G0fC{0Qjsoc>7yVhsibt*z>q_if^^5w z-96F`_u1zgzkC0^Yu&Z(taa7_WQHB@`@|=nz1i~Uq;UsQ;J7|$knQa3*3E~XnbXTG zcKmdgF8;!S2W&Sp&3X=%Zly=csQ8RtI{4*4o*t>gEG`QjRU_3E>-OhD?9pqCX0mq# zw?(|8_Etz>M8fVaPB#ZhXmg-QPI|83z-vxRsFOuyzPGHc)2uob5e*2c@tpsPm7QIv z{nUNOoNq;wQbWDtKZ}}%7jHBV4Kb1ERKABPr65;r2cUDS9jG3Y|!f65i_1(uJ zV}AYtmB{TZ{np5gw>-j@v-iuaCw!M?&hn?Sr5(l$&E3yFj0d%)aZlPeyVd@9*p%uO z+rPft7|eAk6R=xOX@^QsbBnwRw8qiX$@$mP(4vRxQdhJtjT zKqO{6>uVdJzSk)QfGk<%hgBQd+R{te833B~EZQzje;fIOyv1lEUiI zk*CDUqo#@~Q5ki)fOei(-jhb5)|eyr&Y5qLzMnq_%_f+F)DQpu_591Jcz-J28n3JB zAyAc(yH>9F(&)Ex0v!A(H5LHt`GF}*-KOBi@a~)J=(j*z7>LNr%5sYwY%0|hX=&(= zRl0#AQdV#5`1l9AL?jQ^Z+!e!#zIlkkJ@Xjl>^33(bBcwk{9Xhz9niN^({C!m|w@#G?U|P!>g%+ zlcVtvI&6E zPc^ewK1A>19@?3XnFDG4OkA8C8W8=Km?N=SXY!@rwPYtITO%=imeuovGuJW*OLqO& zt{I?WZGL=T>)Uea&2tI!Ln~dSNY%}9tGo=Hr!Vqd^R`x76wg%lh6H#%eW|Wls%+du z)!9FC+9sB;vdUZQ)kyUFaAuz}j7_U4ieXBSirXp)xppNdxx?%;2C<=QBSJm7?zt9( z(H4iGz(8LGI!UxyzJy3v_}X#n7BeGbBebYHes!aMoJF5L_Jl9L2c zwRU1{C=6AoVkx@YT%|X$0T+YWv3Osz`orj3H{F`oZ*di^+$$Ta8J^~Ht25v{@ucv49VOy2 zIKMT0L*1XIWz=ELO!x80aotSwUUqjFyB4Q}-)$obTv~aW9iN>p03u#vX#3(=fQFY7575z%HGRm&J#DYMrdptZ)mz&6Y9GMT_ zO{JdQ@O@;E6`Ocl;Qf#9&>30IkAG+};^5$@oBSk7@yAlHzTBhBHP5A_I20AT{I9qY zU4D4;826*trg5K-d5Vwi?%khi1y&{3cAHCE;P`{;J~~+Yj-5V}*(-(Y1Bm?`$RUoi zy6MRgkFJLy-{rTsZO&i&=ST>9X+=LZj`Pp~4J1$mg~PPGaTe&_{-8bM@vX{Vy}RwB4(6EtUR*fcw=7k#T@zY(i(#>$XP z4~sB3%n$>t!>y932GQm9;d(Q>1bAxeW?Lh%vl=+V*P$Ca`5!e2jSGXA*SG2_E1dki z1nhFq?T`#ESwS*5+UnC!tcZ=x1w`Gny#U!U1txvBkR4NXoTnX7n4q4oF986jUoTdJ z5=s>w^Kyqi;mqgPBmruSWSGO~w??R!ejmztU7@L|x%Dp93yZc?#-OCOmPF%z*Gz=7 z<|B-R9_wmOPJVv3yB{^m)O2*}&)Q$V3~TnUAcgiKUZzZjx5k~AM{z+hd9z(~!2YLd zKf3SvQVRd>v+a?mCocD&0RB6(Zufmz!V5DS>@GlRU4c(X_GNoCl*GOvw=j@s5UO$& zi!J~JJk`!?IrR0d!2;_rS~@ytFY9OK-&Y)TOxF4|r+x|mL?PjpPg%H3@Pl}2VJCE} zM!r6)a+an#N0_cvMe{weZhcv7VMas4qrvsQo3_sLca;kbzv&tnq(ZJ{hQ5_SKISDL z6Rrnass%7bNPcG_$c(54`SiYad4ys9)fg1sy|X%GfH$@#j8+J^9u@}U z+*;_aU##F45Wq;b$1o{OI@*`)VIT1?4DVfome=H9+bq0WE`~3@qr+vfBUj9S>hEcL zw~3VzwY9aieTE8|p6}mptqhgP%WpbAWNN#-f)gBRox=lQxJ1)%5ubp-B49}fw+EG-@o2dNlVZ~6 z7)q`O%>*;MF?Bpfd8V(j!vFTgKB(Uw6-9f|tOo1N{{>e}cx^gJT24;ox5`q?)0QEr z*Z(XeTH2m$ofDRNNhsXAbVo`V!JuC6PnFN~$lB7fagFz$Ae6Yq&^e-U@4ryLkNZ%$ zw{Id00X)Mcr)w!BJ2n=2rGoB+nYYiMe{PEwO!X49E!A5}$q5E!$^%h*M4)0VD{Zop zk5>lu*oLY7Gf)pS*QX}qae?$Q7>%7(Z(rXY06^9&bn))QgdYEWE*QPf>y|TRA zv-eH%l8v+Rf`o|Jh=8r~U_ywiYe1^mNGom_?utT3-4$;kYc$@ZCpc1L(DFsHbjS?I zTkzsmPwp|$1D2KuEUiaVC~Fvhg<^6O)X*yLcPCnIUJ}1eg;d+MzcdRMONT~BwYRs8 z!epYfG=ptcBJewDn@8{DXysypqobo2c@19`$N}bs+BtR{$Vy#q8g4FW9vB$FNPhf? z(N0VOhDt*g0E8&-o2(K1No_!m+Z;RNm{c(L0x(c6nu^x|Qe3Uf1lKW@DuTRgd<8yv2TWFPcxN z&2@Cm^RF)F2@UcMEms1FhP^7Yxoc5w)x_q zt7<`=A1RboHMB&3wz0z^(yDY;2Wc%g*$n3CCvPhbtrk)H)hK|$Q9I@%nRX(wkzBoi zlGx0)EVOtB0jECBs=;NuNOHw-Rb6_nZFouLmpuVfjrt)c8HegEW$HKc$; z0A@-5w^TZFDu!CZbmOg+g4m+dUA;@wY@|JvDjZa0-;-2TqB{rA^p3n`tZ>qoG7c9h zB>26vu|d6koAgF@;VbB<#@6Qg300pEgRZvjyhVhRjY1YX9K#-x1 zJwe!!TC!N?`}gZGKE=p-1YvLq+aq42dHZyU6q^F!oJmAPF2C|bq}l0KM-{T%Z8zr? z0Q~}0KsUzeSduU3JD(Ic()Y_GjF-q*xJh4yaWboOklF~B4Fm;%~OI8E(341ITn(N zU(ZN1e%Yx$vZO^!Biu})tf`2>-*P6mi}6;4FNIjyw(YW2pI%`S$e2-;+=6%5^8YLy z8mGjkDbR77?#A^VE@%Jx^(;9l$q-ruTb-3b<5sQLC$~>eb_i zEE^5q=rEck-owpdMG;w>M7F&@)s7cm$Qk!#v9f3H4{pxIhL3QW50*=@v9Xodm3$vN z68EFv?s6YC1MErC-(#sMM6JLSuXu%mr=zmsfaJ!F#C(JHMc9kR6H`HY!Z%TJcL~u~ z?VF1Q8h{>>t0ytv`LQOIEenIe#zzZBmCzws8|7FkRaMmjvpzXu;mwz` z2#}*vl?t1=cl@pxm&K|kRx_7Yrmuy3u{S+(hG9DjF2k=gf(rYU zU9GLHi+t>5D@hNdeFRcD^_pgPhjjAwWnm?~C7s(VC?c8q1 zr%OUu()-{PSj1lU0Fi0CG#XptCI@+k#_i8^!1vds33;1xGTcdil3B*p1O-R)1;z|X ziJ}(&ZM*%d!V>#j<-f>3;Bym8ya<5+9>w{Pu{A)9TD(^Q?^fZJ(aSj;X(J%`$L*kK zAqUR-mCm0(?+!Tz(VRWH-1K@ZJa)=RqtMOm`&H^)25i{^&r04%>dZa5VPVp1@dCC4 zRU#kC^S6EYn%de{J;hq*H#)sbocZ%%@Aldw{?<%W^Byab#ab+a)y*$;i;g#XYrf2IXm6H^`dLvB(Q= zp=SMhwaP?{+1j=>cO}}=iI_y^wo4ilJ5vsAEXz}|QaW-ogY)AWj-EUNF3X1 z8X6w&OSZovyrR1OHccVXbJXeNOhzd6>`fW~hZRm-UAFd@YHVR%G`I0Iw$JlQcSk(? zce1byxo81{JxcO`)ELJmp#xTh_7SOCpFxw2wdI}+mP`|GvmICYy}mlEt*>{LSkJBB?#2W6(_bkD3=~S%r%VfQB2L0^oBxb> zHgJU|n;N=#nFoyyzSj11R!%w~mug?%c|^T3xToJXHyL%H77BR=wQ-Y`1ZSCup!-ls zUK$_`Ox8IHtvTHaXPFjHkKI|Y9RlO9p~ zBv{pfK^R@+5u;;=K0eS52{e8{kNjrSd1abpGF zi3hZa#P>fnESX(g#DEDiUf5}S9?-{2SFYe^Wg~mGI_s@8>pvC9=cB&r3GkDK=&}uY zssj!jo{^CuV6*&nfOD=Xgc-Pc1qdevr8igw_Xc!|OG*|#oFg_oIev&FHn)?bB8&?5 zzWvU+@gOc-E|wpwlq$C{-$|EOWdCwy@Y9Xb2(eKR3ygMxE~TlMFHP~Hq**}29_@)= zaqw46&0Kc696tl8Wy3zD_DiIC16|aNLb5juShfsx-=&1uHC;$(Z|8UnKvluuiGkny zsVmXmpF0rtx<98cvo0v?-ue2&zSyDoH23H_QIxg?jyYq8_nA>OVZf{Wd}8)OE`NPU zAQP72r7SV3cx}onQaMx2a$smE2Y7UnQj2V$?ewCn7Pa>Pi9uxdl}Z|(F^Ex(n;8XDsFZ6n2(-b zoU-=>qtlz!8Qz^b4V)mU5US=|~T#$SC(5rmMxS09$XeKi9241Y{<$TSrI?pPX1NK|b z0CD0x7VY?5!}-s1=N@o!Dvq^9rL$Mi)#+*svIcr}8w;5)gl*tgJW#}?Hzb4hRxT`Bekp|5+ zpmtfFwhsq}#QQT}vQJo^om=4d?{W8h%&9jt46M3KZA~Cop(1%Kx+ZG8Yyce*1ZKx?!U_g%YNU5!I;R|xWlZ`=(XWWtV0d{Pz`Gb62^7X6KhvhIx z*tNb|ONwb_GA@OhZP1ZZ+u#jRHYuS^X-&Y1UcIv55`oqUsGiL+GMz5aClom1naDjA z9t@%7E;(KvhuAn;5!)M>+{~rFtcIu;0pKV=&yGAO``X7jRS{VYQ{LLE-o7>6->Iwa`4+Gu1vRzr(qU{^$Oz4?SPy_c3hW7^EF02_C{*y9H*d^M zGJ4imXFww1KqVUE(kF%32;hjUDmv+>c1@2D9R{)@4>4-F37{%U_3-$UoyLbRf!*oVqWI#Mo#dOWH?2LhgYinA>RZMMcaa7C4h0SfyJ#V`Yz;SC&_l zo4XNhJg7D8Lt&zl-Rf}6OttPgCnqQRd%u`Lk7)fZFeor@<;j%FXJ4VjhDQcAI?<(z z__6>2ksc|(a>@)xKZ?Bk;;)7l2J8a^3(hvZ8yNsSn^yIG%(?+OT@Baesc$oy$#()o zb<)(wO@hehSOo2sJ5pqgD#nL>p=%T|qVTMK=xeG$${hQzQs#~n6tc>|5h6M;L}~o- z<$(*gY!C7iF8Zj!H|N6OmXiOkTB%uV@-*-;4 zT?2Oivq)H=NFK)++ zd2u<~pnW=z&zw^``v{OkMr?&vZX=~k8d*`U>P2#ty%r5!oVgAgH<>gFGeKyk z6-R{I!%~(2wBNbERi&_QcW zxvxt^X)R%UA0tU0eOpY#cF08sTy<9_R7q{Q&B_i|ev{TR zB%^J2ob#K4Su?aN92X4S+^`yDJnd(^KCX7J(^WN_j+TD{5u)BeA!2&ymKRmGb&DAj z2#WM3Wo`sQ8kjwC*hQ$;h=TQHtK2&WKERt|;GQ62H?89LPyDD&?%KNQEBa0y&xjucIpzo9h$(q3&y+bg|8(S~q zf2cQSmm}P(pUzZ5@8Vt-7DP?&+yWB{-qPFD2)vPp)sckAPFv%Cb8vwu*h<20qoRghy=*AQ#Sj^*l>L30qYSN>2`OuN2&=i{Mg`QAnQ z8#iu{QoE|vdXu(y)oQy&iIB!Kt7c;)TG5!3$7k>x7>lj8Z*H_7qaV7(K-^9z zB4e9hF3Cr4Y*4_>diLSN2V*#bdyIGPJcdZ9>ZIgT2)GU-s1Y`1Tk$c%PHzE7hd$Q? z2y#SB%cNC`!zgE@KnBEW7f5?ur3!|OaFs?N30PGoMn)+Jx?OYuLXaj+o(ICyfVP&h zKOl&DlvWh!?c14y1q7s8SAE$b!HUK`2Q4wPh={hN|LsnwIk@fHZgX37P8Qmp35{ZE z9kG>)(er7_R^b>JkfJ$q&<1Z!qg)peYbN2Bzw8U;?6VBq27UCxeOxR4p`g<@nfjJN zG4xZ5o3)O-;+nSh7C@$HwLUkE8YgQn5fdY842jX%mHW~<&LU!MZEO)-MuGTO)Aa!~ zeKb#=NXtqg_+b#d5YhpDPZ0vk%bnGLC9uxuSy;q6FtkCHhI8n?KQJWtfR^^D)An*F zaek$$4GKrW^}MY4givK~a&mm+c(_KbGA5;{73dSqG!DOv%OA)fe+bbvB?buV z;|KdYI~8E}bwfpgd$VVyB~zLLw`cM9gcv&a%_rCG9o1$PEDB&gL;~)#vw|-#u_n1@ zsVF5S1*g+DDehy#vu-;3YKbi?tMuK(N0ltK_~tP7g&9?Cg<9`EcHCP{=i`O?v->k) zwjfSY1uzkjCJcxk#-+$cG~_=2#$tO9Do_UUrH6~d{IED>?d-aKo;lCGR&#N%lv~aH zJn=WEbD{GkAtOr#^b*-~J28~x(9(%PBj?>8qy-{q-Rtjl3kM;jO0iexS_QsnFgk6z+SO?QjxOO0eV z1ii4Z!kFw)g^ZpNX%tOoXD4Fn;9lQu)6@w>VNgb9YE^({Z#!<`Pt*(2b`J<6gB4Ep zv->Rs5!N!o{TiqnNq;x9hi}Z3Lan2E3#}fZozO3Njx1eN`S4w&D&-RTC2({lg zXts!MOlM;fz6XpzXZ0u1gLNg+J=6ALGyxlLwFG@2KR?xY@t z0!nY};5h4W`7d}z5QrD?xtqED#55vF&_B%gcP1k0HqhTDpT;)|*8UuoqcS1uPTobi zRBU}@V@NXI3ICIn-8g&Z2y@d>&BAD^Zp?PN;hORTox9eDt-kfOL}Vxs6@qF=8-T^G z-xD=kzeqZnHN*E8jVT1(mrc?v47%)Qb}UmS_H=rAITNtXM%c`H zP1DXba&{YiX$oR9%&3O-Aw++L_}mb=G~(nXtur8aGt`@k7X9nKvK~ z%`}lB;+bubt3$e61R%?#MZ;pDXv|d^JY{W?k|qmX$@{xg0f?ifVy!L`^UBgWVRI^> zpE(y@VwH?=Ifzy6JdJY){DT~O$EQf9oUc5$J z9T7ZcDYB8g3#l}2Jy1Ty1C7JDs2Lo}qMZq<@daptdl04KhWmMgQK8^m3u&Txp2g}~ zB5$alAU_%bH!m$Ki=XebNLQR`+8bpD6WMHO>+Ib3OwAH20k`AwP&blnn+#H6Lt3rQFv`KC? zWY0uX-(A!-R@Pl=?v!(i_rvf5;s6ZbbRD^gk~YxVK+>+kWRx6wlt~z4?3Vkp9;M1> zJgGUaoc{Nd_u%`1@Q?)}vz}rprIP8K5k5Y?`Q2Q22AQJMDKx}OQdz1i{qqGRB^D4ebn$qHy1WpP zsNk9J{ralenw^a|>TT(HR3JwRI81slsir4Dj#@IECPxHS>oYQg`A$WEMzaF3w1|EI zy47Zmquq6|XbISDKA$a*ciI8mo({23GBPrT6WFtDVJ4bseVz-4x6}*eF^S~q>Gx;I zZV-k0`1pW3GZkW^5qMC+uL75{o+NU#f37Ti%xDXv4AfgM@QdkiBl%-gNj9TO)pFlJ zSDnsn;@S(v`&$DBv$0CCZqYrpkPMW9>5CV$ds0u@R;(4j9^Ms$y;}fgoRxkM%SQ03 zwll;u6!;hM%I~eu%*>Cc!FE^ED$*=8O&8hUrNeKmcfnfH8+~h5xf=e{ z*`>L*b|xjeX7qDYNCwGn`J_3I_3Vp*Asre4#vO}e9e}pw0))4}hq7s57-E-`Z!q{T zb<3xkY^Eth@LFU&7283O_>ve+#-r-4&%C1@$bS$`!Has!kD6N%mb=2p!4k&RacmwO zdOapJo(moEVibaQz9I+9O3;RBx$JLpuq*D2xm9Vq?vYZ7I6v1Nb(DGi`n461SkU)w z?vd$$hOiA?;aDJ1x#v0R;FJl7NG6+B*{s7i?8AbzMcVUDMly~>qxq_VTXVU5wpmJ= z%=@ZJiqeBxykt{Kh%uf?rAK>$ENh`%*D@UYS5egGpo-|8^2Ju2G+P^+p7EbzxObgz zpmjq>@>+C(uPCEkG&UZaS=5CMIH805R=6%xT>|3}5z zY$QA;4>im43Q8W=ql2ZIFE>I!wSN(eFyAaHm(tp zN-D@FfrbuST+AmMAw(}Dvs@Mu9ldE9r0BSOd_7{Y;N#WfF_*z@D^@dso2eo3xw8ZL zYUC6dev*@$^+epZzfV@Xd$LG3`;X}1O_W6T)=DK-dSJFup%SoZu@lTeeRV;*&e(@% zWo1Qh+G5UKzMW>KQDh9uX8*V>+;KU#p6k^(q(5=jr5EO_*&3>(Oe&}^*Xh^sGjE=3 zlYx{Ji2Ra!>7dhBIWN7_m%*S)M&{7Fj@nwpKS~|Tkmeg$VimPt5sZxE?G;}{?^`tb z@j0$VXscPG@zSK7fqhihKF*(cb4TCcfVKR|&F&xh)X;1)HR zj5GdymfN7x$K$w%cXCbx_oixZJ8cGgpOw-73e98dUjg+xroDl4Aa zt#loMr zVsEygMP&{o=baW+M*wFpE_e;(Zw;E%uEu#G@4H2!>pB`^m- zS`wFy;$v)Xz4~d7d|e=>Qgyl~ke6=QkIw<{`Vhq$Q}e>-kismg&VeNP3=kQmi#@H5 z=AmIotqXV_%*H^4-pmMX3d_F3TNLOmU=O`hAF9-bJQ}*hm7zkWw|#P;JZsK&k;H>K zcf$e@YZCh{3|!luZ8Htq{T@a{UyLqna;TaF)Z9zZUZE8ogE()Hgb3+OwP2CEC+|*`Y}%U}7MXp4mq?lB zhr%CitvV5!8wb=gRIokZpyGlKNnBELVE_=Tgj$((yo}P5wsfl7w>RPbAWH5|D2lio zdmgarRK7@*s7k)cidJLg6?1K0u?`%AgfqxJ$Wv;wmM~bb(Q5U=J|AlP9=9K-T-FD(4xy|Vt?bbfwkIbrs$^=^K~l$c3vACB25iTD7et-az#30P!D*8Q?x8_V z-=~FZ4a!?uB=Nwx>3NdQW!+l}&^6NfRgf`(S+DJO?Y9Tzz)lGRZrq4?51&^#|1tBa znAp}jJR7p1BYttHD7NGl&yR;mFvr2@&_cnn%)P7*$F4fQD%)P4JpW~nfy9wV;rL8O z`i$(=vwsWD0Rj?mER+rNo61-x%wK=;_Xo#GO!p0Wk@OE|V!6qtsBr|-1c4Tzlup+f zuJYEczt-m5-r(m_FR-p?Cx%4$5A`P(a|IPzJPP(s2H<#j>DNbGv_D3AF(=*-YP!`d&sbW4XJmv+CXF5ZV)_k-w{|iQK zSb}I%fEEy}R?n)n##dgh{#yJ!5N5}Q*(BRvYF7B_dNqdy>WB~ywaS)Q9 z{*LTK&azL@mbLaJDesbCc5{h9)7_)*w{{BMhSqR;GNXDj-RS8)fYlvE_JT`y=F>5F z3^UxyR5|f^aNr?6G2?>8#nHr6^e(PEwFVG(CmzLg&)1L0ra(86x z?n{}1ww70-Px126p?oIon?O<+n^xFKRuhd-ph)@wJhI2dmq@?cTcCd-GA4v;?b|vs za1E_Qj7%c1nL)Sx(Fo^9QNE0eaJiNRe*>XIDkwv_+RvRTdIr{c)Ih6(=#dNm#(mbB zuPPpO4UL`JwoS=YXQoCsEres*E;uL!24U-lZLOIZb*i=<_rohGelOp-^4{)ijB84^ z^U+{)>&obfS>^k+kN(sb!vKzh!2gP^lKIo8RV8Q5C??e`+WM!|H?DwK!a;OkJ~^nV#u?+KLCrJ|wI||*+-QE4xGizt@b`LmxUg6c>s-=(<*vF9VM*UTP)D}3 zaxg>3$_m$wC=t8pW|X`3M$O4>vP-~hOw99a;i>bnhKY<~`@Z?2Heq#1VqMO5%0tkk zGp3oMcsf`J^i~@fHi5rvWrye{xsUJKSIo?1X*|4b5TD>P`>~ADKb3$YYYjzIaG)7$ z4I((e)G3e}JV9ur6022s9J;EFO)wZM8Oc2(iVV{S?FVEbd7AbOU?`xG zexs|_he;>?cj9s^?2^WS35}uIEI%i5kh#3NeM5>w zpwzI=OaY6!rpz>vChA|uC=zBq*D_)LG%sG%Ut9Upq`y7a@KKSEw|AEZvB*nJ=koh< zQ8q*D5=W8B?8?uOUD9bWmMu@6kygm9$z3k1J}{7MD=(NStBy>{LViA9MCdD$N8KMT z7+;=Dh~PFhuJPQW*yzYM&vWUk>N^zKj*_Chf4Z1*)BL5Pbqprq z7l~NDy>{0(qCT!|{L!jh>DY+W z)$Oih>o0?DYzj{#r;u@RGv563?q1A>&o{cH3DN&EXmz)bcD&ZiehZCB0dV(GkycOo z_j}g4_tHUra>Yy=ebQGLCC%4q%?I%Mzg~Av|M$^Gr%vTi&Hf#h68N8g`~PmnXb!j( z9?F-K!(;|7`TvYuhog<`g@S$~X(+*AV;0)u;0hP?(8yEb_y3)&dg|1tn%lo&%Eb@0DEd%H~LpW%o9{+<8( zIiOE|{O1WE@KHSfPe!Z$=f;LjwJKd2Yusqup9qja7ad^yCFmJB_*dwHSy$s`W7-^3 zLLvotAEYGs+s%fZKK1)#dBk1~RZns5xBWn}y~49x`(GcgDCEBB>whDgsi1pZhHLwt zF-z{VicUk{`@LxjH=J1d&;A@erLau$1hd(ew?o|Dy84y@A}2fO{>AxMbzIr_(+zPW9I7yM}c>*|6#jwc^uXHm!l*9 zhvE4@8O?>{#hs0LGG%MkL~uf$xWd1`uLOVj?h4m%dl}iktZpqNlkFlRXP;NqHPNI{^WBVIIsVC8T~qCajoI) z4-2x%oauL5=w8n1-ivlQ`GFV6n#Jy)*?!OP3f%lX*tCEO9c53Kl8?wZivR8Pl>Ex? zS+mm`bx}L=b%8aV^W80%&!?bznTm-{(O!Glgg;$Yndb5JJIjkPst-8d>wbDS{^yUw zR~5>70zW9f-Knj1?|g?b|9s+yQy!wXeo`tB@v8RsrQ&ry(z5c&Px)Uyd(SklsJ`NU zeLlj)>92`3#_EA*s24AE^~N0SMK?8;{HJj*CRmv0R?pYQ)!{1M?OeRp)-`w(cZ;u8 z^z*?Jj%xM2$)mN23jy*^@-2vOE{QNRuDhrcRvDr~>fsYabpLg6pU;tqyxG4=RT}>J zE3f))mYahbMaCGu&r;ox^Mqa{)|oCU`C;5A@=_ko+QR0w3jyud+t_Y8d>RNF%D!Om z$EjhLr!RE}N1v*CR#=Sc*EYhBb7&RnL!OCm4*j0lX1?GjD=}a9t+>ef;WCS?htC~; z)*DB?r8UOYS3^Wc6ZK?XGkvuEaQHr$z^~niKmJfqtE?H)t~BUeTK_4@=z)8^~fjny|USnUBXYH%t6wo z`0(rJhh6y7KRd&V57XYBzi5XscPFoi9N*9XEJW(kXcXRWGfMNwk@tg=EoF$qv!tQw zDW!Ku>VFR3UU_Yt#2yu0|GCiiuOi#qlZF0bKL%f4_###PS%>{$gA9G!2*aOO^r?S% zzs8d8Xb$&U-s(*$eAmf|%`wV+P&w=>K6M`btn@K;TD05vxzoK@m83R&&KHre$x%FH ze;^d|=wB-;!9Jtq-22#12IK0_&UqH{czyfuoKNFt(=*~`&tS>yl$2_9CUhUqQ}?EG z#nZ8u)h`m)GG}v%zFR42+GGx>xpqA$$vR{)d15KXZw$SZF(zUXF!jhO6#e*b=~y_Y zKabZL9eJ=!QCr$M`CLEYSSKh++*7YztRC3B+OHllKDz#Mx70v)f+d>vB**aCkbpy1 zJLVeOVgF~5=k(E)WBTFuMd32-I%hCwE}~FnqB=XKQ4BO+nHQ{|KGSPH&Q`^?{r>f9fuWl-i~x(FDtXHlJhrm$!L=X6+O@ zPlfMfi|m~%tC+OZH&;gG1zs>b~=V?SzV$h|i8 z+DFc__`u`U@drvi;i4kiAIv(ldT$fnRMfg_U~n<8eSVS+30d0c%ZG%j?6|*q_zEk? z6F~S%2gc35|x$y6;G13G%tMf`aIgjM$zGRLQb<9*^XcvH-kKZCFEn%PpCsfJv!{0Sxg9A%r(OHB<~kGZRRhn}2!Ti>U9 z|2orKO2_9YU^a@nCXq4 zWP?-vyaQL{>8|%^%NzEe-*fnynt1hU2+2-{^KpM`SdL{Tv#Wq5O-z8c?%H@LvU{Yd zwfk2t?#PeudqfBgHvM``f5I}qgNt8{Fj4F{eq6)v-gT7f><3rJ6z{AbZDubdRgSl`I`EVWNUK+z05#SjMMQ2 zH~BrsivjZC4+&&ln^E}Ss~!h;%Uu^6+;ru17<>=6K2Dae#txFMEDfb=T1+^Qgmwm} zraPT`B0Km}oogy7WW74#{EbjS8#Z>d z2O*ZvMO1A@ZRTJ?F*RW_Je^JWLUy|5ley>Lc~Af73>W_6v02H>ku~Nq=7??i_pUP@ z2Mj`2C!8ma|CN{)F3IRl1Q)dr43J;{@+ahDGy7&!B5f_*up#fAk8TejCa&W~ZQH4C zSi&~)&h2DlpZAKD-(zCJcr!yVuV4KWdIl0M=N@=D~)k_#d z+k4dAr?Z)Q{>zNY3%1>xUC#~Jq94+~i9m4P@HIQ!wY`06yg&MJEe_s&!C&sFq*s%- z(NknSHLx34I+|P5mWaBD-dY+tB56(=O8vW_CTF7MOvoj6*{hmPS)8-UrDGj$$M6Pf z?|SB{3iTkmi&nKO+&>IiJ9Shsd}_rJmlw2~qInwgSa#+ml{D4Tvw9t?&Ctl3Lm+)K?f~zka=F9Hh+mI`sK~njI$t;iPr?|^?#QF1%0xSOYFppn%W)mxhp^03Q&c2 zUywB!af|AhS0z}n&GgB{+^<|6xJ5VE`!i+Z*n5fA;@kFt$5kfMaR?NOn(f8I<+8=Z z5bWl0n|u)Z;)+*K_4wN!j(mtz<1ALL?2{W>F>`~%` zX17Y7Jl(VOFu#3qZm^JPNW}ndBA*?@%UrpjMV?z_d)FPagg#LP2{5;P`Mq}oGQi(C zeBuH(wb^CcH=r(p-EvFE))qxs9z^6XSM4Fn#vGHsZga=^zM=II*P8Ptg=lnf_Ink7 zLwG2%>Bh6N+WOzhs*(kL=;)6zPtMNxL^QZ-FScbz{oWPh_5am6_l@ZTlUYNv`WADD zrpeLoU6Nup{&T08X5yG;dWhX=i8ooPhx2~Kb9ke#c5jwsm|P^0cU4;8>ML?8vCls<7is^He65Ljg8d_&y2J7q)j<%wsG35=~={u zc4b%`@2qk45jRAt#Wy-;k2N>G5JjMheufeelPyX|PMIovr&ySR z+n;13{)!1A54?vX z!Hfzb1+y!A)vM&FkS~AOy;#iTH0Jt|7%g#b1cQo<*6q_dKV8)y49zMPpC+ zj_H&5tm>>BNVMlNE(#*Gya21PzqTB{hdMh2Hvoqy-F$JcVgaf$DCh9nyk=P^Q+<|5 zaRaH&s}a zza`aDMil$&cWSNyzAq`9df`}P&J`&}(ao$}vC2x%JxwCL6U8&+esd>MD2?6p+Ln6# zIp{%@=Es@?v{w5gkkF9eUi6y#wPZ|F5A2oVZ7y1QAEpThWu8R)8?|}sLQHpA#gd)+ zrOdBv0Ht87`QVoe9yV%7c{SS(9m|Y?q|~)pi$QAZ$7;8cit)A=MC?kM4o$Pl%*+&Q z$z)VBq2Nc_Gv6gO9ZQ!U;VI||eh(SI&`{Av?A^C{Q9|mj{=w~ZR+`77@5 zYGEly+VN|u;LQ5xLm@oe=FFX}hBFFH*gfPgj{H4aEw22rEJ7quA;paU#n7Bzia~PW z%ylyo`dCzotO%-u-+t#}V>c~au|%Fdzw#y5nEQE)U3l*K^X59A2d$(GpT33Ob$2rqSZ!MaDRf-gYO?(w&OIU?ha#4?m`)X6cX`(HMESJGTwxnYVFw3qkEAkXzO#F9 z&@crCb@?9IUyq-e6Ai=KLoxk%D(;CMv&XM{cR7x#Zhl#J2|J&$UzGM?eEy#J_`N$U zSGRjpDBOr1$9SVJxDu5d+!JCy^~yl+vdJT{5Tl5DbFL%o#M3Kf`!6#7{%A!nYQ?O% zjFlx_GInwi5J0J*dg+SZjQ$*@F!$f~mL_))*Kx(VPCwC{xzwp^f130ledV~}V*Qzd zd`{2j9zs}tE=E&|JWlSG{LE_Md$rn{FoA^b?1g!0; z$QSqQvA?8CU3Gk#L%iD9VIbl9KZ_pP zmwx*#^_X#|Cms&L(IFQzUtGRao;m|PGD?K!qOUY+I5k2fQ<1Ive^K{VVRdv(+aLtD z;2PX5Sb}S?5Zocb-7N&S;BE;UcPF?L+zAkZ26uON{TI*kzTeDu&DmUYG6&fZcK7Pl z)wQbbx~saDdG;YMlP4|CPp87!(ye-7nBPEoMTW*{5eOeJf=F1(!Y zxA?yFYuLAu)N6fkGl4WrG^Iu{t~ry5@MR# zc%-cZUsX7%2ngfkQFPZ^Yd$3r6snR2^yQJW@amZS9K!%E*mQ}s`)lnX4_|db_8^B- zsb~Q7=y8K;H*KB~Kz3o$^v~u|*hIm10Unu2bzQHaxLD+!ZJ>j>b(M6uXlWPE*&4)=pt`97TM3k&pRpRh zuk4gyNJto&{8mrHJmh?7*Wnl9v`h!vIukO?Y_8tR6S#uV*mtV)@fn+e_0(?J8Rgl; z=lRM)${W4;b@C@Tl%sbyKPM%X%6x|N)o^u=K7xe@?nhPi*fK@^FA`$7KhVO8jH>r0 zf-4PPzZMV@_;8;i^D&|tw$3$W3<5}qyi9cQefm8WzKME%P#9DRZQx8g*Ne{8Q&{Ra z$oUb&-9hzb7z<=$r-Z&EDx3Zv4f}L-hybJlmB%8Ak8WF3CHt>sJ)07I#niY~pInjC zUM3MP^LR3K55uk6IH-DY*urk)1V zz~u#xDNg*WL91~zC>}oY+Y?ZDwD1*d?DU`c7=H7P(dvzE>W;>9U6~qOX8g-7h2;Zw zqd4w+rimO?!yPS^fRGGN)5yHn_t?brNZZJ>@(ETnXLZ^Nj=yDapy>?mH+iAZ%;TOIqSw^gKw8k>(4LiE60_vnldg!S zcDnxGCd#Ay{Zl{hPxYn@s5a%%_w%~P=shi&1k}04?M0G*rY`;tq)-U3Y~3PieJ#qF z=aaa`ZLPcePUaS9lse}6Bl%MmE}f3KjQ8Opo{f)!cUi5{#NLtW0z5!XKL}vtg%<4% z#(pS*W&R8fOqvyLGVDEh+-_t0Kc7~1#%Ng1eoV@ggmZ*AEXO?|r?AMld%)GcWZYT8 z#&W*X9eIq4`zFL2PSfj=dS~G$jYB?ObiYoe&&vL@#p_t`no1u?ZvEkD=6Gd0X-%LXE!{*-lFzc zS!V`&4-JhLhHc_snqd-7+jHURkjRZ>=f~PR7kzi}+RVy-wRz{u{%n>lNU#%9(+ggG zjDBwrBGa#!lphJc`Q!z8eS{~gQf~?u>$2mq>s_J#+VYiZ-Pn&NEw`(t1hPN|Keb<_ zU)vs;pJU2RBz4?CHmOqCIY)YGI~{E-WJj-O*9G9vy$3f2@LXUyE2(zxpSXvOOs~Hv zNB;~XDhK>_cT1*#jxD~aPYngxldOtWQfGcv_ShG_0j;k1?iShtoA8zUKNsU6u-EWC zh1$cb+CJm+?8_yP-h;_6^2n4=JUN+%OkHa#Y)|cxsq8bY+a(0`@*lN7yQf>Rw$YwW zQr#?2NhpoYF>QOsogZ0Hmh=3v^FiKqu4I$GZ8)7fy8m^AZ&S1+vyIU465d95dDBS%)TFZNw-08+p$zpd>!#f>WMo3)J z07hze{i+1Bs+yx?=VyzIz%Q+dEi~kxPqPC@-mj&F7=8!W%xwAn`Ji-~k}g+l2-p>( zV2plN!MM2lKQ`W{{ARlY;sbBfm{y!db2F_d=+F<#i5H1d`dr#U2y*60oP9Vg!{0XX zxg)>3ZgmOmbuR<9?l0qc!a zq#88iHTX_X+kN4Um0<#Z?>YdLYOO(SAe3dTb2F4Yq75rg+*qLcBWy2r(#c#-PcP*^ z@Pl>iv^|t-z0`Rk65)Dnw24S>abx}Uht?S{G!V?3M$}VNqa*sX{#D8Q+`m94PSZfO zbjZ7;5vGs(nJM#iB$|Ei6{I&TwNtrXVK!aN&82(*LK}|0z@V(JnbxUICbyLjXgk;A z-AL(70pZikD-b&Y&QUp)>_z}Mf;0ekrw>o`0&4^a%8F`b4aO85WPyzXmmabD%K$M- zyqLN$bRsMHQ_@z}#Z6B;tZ6QqFL?_>V_f+qbae}x=TOr@iv=uPP6)ddipN8}2W+9L zzyQqVsmCXs#;?gWjMPseB0TvS$FqYTq=_R%zJ~LB8}|zGn(r6|e4Kw8WwHXk&7|$y zI7j9FwoWZ%`i5cW;KKS7ZcFM9gZ57@khPc$9~&RLmZpr^%9IVj%#7HTdZeGxwFPfX zsf<6CxLkGZ+;xB{LDb*1_?dfX&yH=jeycxLehCGFh0MAXbtl}3Q}l2=(&oHrVV4Yy zeE{MtdrGROnu`v`Eg zmEtXZ2S`J8`!!2=EoQ^q>hZND9v^P#BlqSi4Ajgg7*+UNu3d5%lF-u|tZd8j(-bFjtfy1;mxL@ontDgV~ zyInuoG7(Hq{tv)**Msapl}o{T3Reg`=X&~RtMEoQDw6{v07q`C%HaF@-? ze?sgmzDI9eo-ye45QqcHrVqrms*T;>>T#em2@tGSI<%(W%*dIf3OE58MbpHy^qE=S z!cY4f)lnSP=v9+k0b?q88`f^=z1~5+5uctsy%BWPGxu3-?qSrpKZ`%iiRib$*Jmz$PB67`NhKjDtm|!kxF|p&xI4oj z%>v@%E-M3{(kL11l?I!v%KlF-1&6??O(neSzA(dd`;%0$*Vn20)IA{!Ihk&q-VDuo zezRRdsDvMU>=7|t7_xVF*+TJjwz1NNix@ZD_On$;!}=EpdNJ}?M;4ak&qStgFcsAmU85M`$v)XO-G;L;z?|@-VBHA%pGUkTtE%R*I{odIN033BdxK*Cn_z%Pd z9xNcOum{MKjt^dz(^t|P0jp0FCFI&TzjPkAf2RX-!gnpT?F>!QDyMMx6|d6Byn(c6t`S-CK$FKW#tFEL8@u}hFo!R%zILA3U_=_&AVu*CHuA99bFqY=Md|k z|M=!u##q_>qO-N^P}ab7xURJ-Hs?XBdGDm49l=2ZNd>D!B8w=wLF2Cq_IyH1cu~vS zvL!@H&^bDwv7Q{zcTK=NSS{|M8el|Nt>{0Ik zzYjzw6{pu;E5>8&4qAOkbPOPM?{`r6`3btE> zM$B(o9V}0E0ND?a9Wgq)ez`qU!#K=6>cRMY0<>=a6)~h#?!^8=amDyQsVjfzMJx=A zcwd_L*$$(^a62D&U0SMYVXJTBx)AYE2nZ`?SS+@@6;T0;6Q)L`t(uRfxfccatO_-> zQGRmNV=Sc|EcjPN>j8ZQ`X^cZnrETm*&FH22^e4nqU`I&hkVIkYnS<#VQcgS7mJIf z=^76~g$*zv7gT|-Oh=qFbvOSsgjly}=-ek8NLy~~MVM5`;q^38RlQXkZ|oLQp0MwFvd&42M)T zbXvbQ(1OBW;!xf0U&R$*VJZ=WurR6JDOU`Sa0VH~RWUYA#2ce`5*KN78lFNzzNG!% zYd&PZUfs{U`86;g8Z4h?U}#90DklyL1^Qt4Zf(fKNKxC-{7U(j6glxbH39osfuPth z!u3m7{X!p`4%k9Ba3~pH{&)Dis?SfLc!D(mS~sm<=iz~jtwBZ{jsxro1cqKYdJ^F& z7SA&=p!zENETpj;BfNb$`rgJ7Q>Ult<--97v$i~@QWR7khsVLo- zc4WVDQ&IVM=unP<7v(+_Id70=q(@FqSXWp}0}ZLu#Jq zf`Sb69#=-)Sw1N{w=z{!+z$JUgL0woi|moI#RBM~Nk;U%;E^#h(uc|d!CEa8H+iPx+jMjg@N5OSbQv1#L#TzEr;KZ?Wp-M0vTbSdTf-H~=N`q8CbJ zcQsPZ#RwPQR-UWT^kMlMaY8E*7WZ43F%P4~Oq>!^9K)dhTTcV1?zMOJ1%08v(#YV6 z!79c2H__10ylI-yRg@OBv_x)AIl=YTt<>b%OXE2^QbOGuhvP1HM>fL^m!<6j+vTvC z+LJZA-iq_2zyeUub{ru^-DK(#6x zD^u&3d$hZr4c~5H-FF{JaqE80UHXv5x5W%z33xW+xiMTj89UbpyfsG(s7HH2W(qF? zs!v_9u_HsN;LUZmu!*oDW>jc1^|mIjy*F_LJg~l=bln=9HU5o}cI<6x0n^x9^3e+O zc$VO=#W^TDY*bNNm{Ag{FpLD+VA)l+v-yyCd!-ujM|H%S&ggMX^h=D`wtf&1rA4$qB} z!zAuEgcWK25%n?YJ;AU=)}sCoJZ|P>myeZ7d*pV~pRvGim~`j|VwTW*ICQV^%ye|l zMvjEwueOiXh5j%^G)!}A-gF6cOTJ$#w%g3|Dk%H)`?rX?t$uyUUCbc`W%jr8MR?aX z3p@YN(h~OFBMp0>Z$1=~X}~%Qr$%>^)f-lNnB&%z7&2?;iud&ObUdtoxc4|O99Wte zFK}3!xQ%Y4&;fgek|x{NA7+zPGi2cM5>y#w=VaTT zUOT`Fc+#iBK}ku=c7JqnnQ!(cnXyEs2G#03j<-0Z*EdLJxJh+)@^GM%8qINQXq@jC z6Z+wwG;VaA#s(y_HYeaUjHSX8Hsd5Dn|n)G`&LF;7YcQYU((8@L^|ln`fClhvc0Jn zajrY`DV?@EwkPIs!~A2zk7#B=^M(q>qt32(GE%r$7C*e_+VIwTpU)Mmc6U-G>Akpk z?jT1lhp`EMYqQeW2F}l2PAh}Wt8jL^6X}RMLn6z+MZsqUk3Cm!=&1NOL>qWEz>gPa zZI3Tx+R{P;u6Y_Rwj(VZ@iJYWqOKMIih8H~Z&RRj3ihX92uu z{}rlYU}(ysEe<#C&|%C$D8QO3)-wRXJmJH6$C{3U?`D=Rh$JGlEF+tGi{|eA7DG^o z2nwN(cG~Hfx;0Vgm0_i3Lzw7iWlHZG3kG@O8!eu_Qik1UQ=8>s2h$91i0Qp;sg+A! z&em|p)w@_*Qzbeq4XqYQx$}c5u>TFG*aZ=%zJ&=5 z&<@p_YB6vE^1L^A>ujVzPx!6*v?31*l)kBL)S;k9=S&ZryCpJZe0==&XfZbtr!8}; zn(^!*zs37Ga=VWxhc86j)%)?7qlu@(1Fl_5wAj^ik zd7@DH>l}tx$skuXhp*jyNVHHku#j+IwoTwfTRpfD@0`hEv$~ z?r0Fvkb)9|H>-$?!XjKF1FH*Gou-+XKR%#v9ZpBH<`2KS5_&l9Ip@7T81{KSM_;AY z&t5HCnF1?xq$W_t_noeReZ2(=e(4@X*f&;O6A~RftZS^S4&Ptuj)$Q&28NbF3Uu<& z$cU$m2rj~DJaDx6vwyAdXyov~p}3fjMD^t!M!)Ec6f3_8vSc`Fq$ISWC}FO!ad7#{ z!D+3OyvQpk=sH>?$t`G^TZ9$L^#~0q%x$iR*2Q7}b;sh@Bz{!w=htk)-Vi?DoSB=O zpHF_hfudUe@s*m@1kN8?#%j2UQ-MPS@cmtX^_g`q}vN)%2-6XlzPzu+ZNS)YzVDE%+;lu0;D|vIG2OsbP43c z?2sE}rAdn05e5Xer}-+<(348WGwd~^ldbp*5eok%dc$Pq0lvoF{w@RznQrs_i5Ej@ zI?G#*2qz}S4X@=oymd|~7C>l1v(fVMI6LN@F1$;qbZ|a+%Q)y>dVK5X;FjkEHY)Si#f;;BcvZeFn zfQNK`Y{fJRK9^XEDrGBdVp4zo__F^)DB~RROV7}}r9Y|>xZJrKbo=MlQ@8dqk%L(Mnu7F|DmKJ|(M3WELqvE$m{VFdWd2;vbKexId6g6hqY zR$=yBv-j8YErBS9bT~#&{lotGMjG|y&lX@LMjx`Vz8Bo~vS;PNgYB~jI`FQ@Yrz*? zLsl9!kGovFuVDtS(#5_Ht7o0|p^U6ot7*1g5d){K8ZRR&*B9;a=Ld8B+Bupv&puM{ z=-|p7B`OpKW=aTfwY~W{x{P};?LX(B83#oznqCX(`45n>ij*qEFArusy)!P^7*R`Jsh<i4a9HUr;40EzRL)IoB^tMOiPq1|<42Ct+>=;AsHzIVH-yh#4jCfGd`{xR_2mvbVXnvo729Ckg%0X`ZEDnSyusQbv!3{>&9 zKc%DPj#jew#^k^dBYgb$v9kx0gfI%t%3Jm4X@>Wo-5zU=&fuG0 zqV#OPYkAg433t^i(mdOyh_g1-xK>y!;sO zDTBEqal_-7Qnpsc!ory=%HytxncL}s!u49fziWGg@rY}jo49v`%z6Do`0k?r`rHBE znZ~nB)qtFc~q8G3SZ+~V@Sfj%?Ndt)Pn-T!X+cia2e*F!0!CLDyM*YjQb4A>(;4Q^c= zI;xILDON9hoS>8Gw^%=fA|(nr;M_V}2a4T2s=EELIz0>PArLIY*9{Id?At^G08B0Y zemiT)3*ve0F=-0v|(jiE8t{AIPTflQny?|>zWg;o7g7|B)9k+ib_i_;)mf6 zXBgeC_uJe2#Tp{wYp+$4DbZ_!C*0ZTlo-S@h^c2raDf()OKSSnZODF$P8#+5H|p=S zOWrbf-HBTotyf@Xn)Qb4RU91bHkb8@9DDrRp*@;+Yogj0&9LjO5hXfx7+dnBeP6RO z^cPnl1bm-~v#Gjb%f1}BdT4i_55K3l-f)aazsB8sts6Ru-6m>iC{UIAq~<)|xEa}0 zZ@XMC{M7r1jjvds3)3S&kt7>Lb$7MAn)IE3%l*YB%Pppy7SGyo>%#Qq9-py^Nykqc zgx~kx>_4VT0(*x(Jzp}h-j&93>TmNxaXLQx*`NdC-R((;65{by*L)H7$$D(tz$sx; z(sZ*eC#leJN3O*ic9Y3^jL`E6)BYC%-q#`~ADyt2$Jo683hfIyq8NGbtzCWWI{7e# zH|@3ypI;&zQgbfb^BLP`Hd?2^F59O`^4e?CUY2Yg(RYE z?jly(Fi}*f*X;7|()Wq2CsOIs*e2<%!^iz&!UiU$5l-I`4A;&(3N`-Xy~F&W=Yeu_ zx2VDo&j(yy>VI6`FJlf@ZJuz@DxIg^qCC;r^@+4Z_msqPqAYj@C4Tj+=nWKyz{=h~Se04N>OU3Lo0nw(X6Jnu zvA#}yfBit*Kql-b1QYZu^XT2t3?=hfgf%kyt}LujE~aO2Pz6`0`IDpd#+jW~_6A-F6H~KQ-!}a;j*qVV#^`!X zhmB*Rud&F#!giP3*ldu^bBD$CR!D{UijnbU2J;-;)hp<0%Sr>Ghm5{dr3`<))0p$ASjXy|cn|Mf<*T#m9WQkinEyGt92_0UE-n z(R0*R(y{3zTmhuU zO=9kZhg!LEKHjI?aF;b$vA-8NT@>Z0IN{vrd?3BOm(B{Eu&e++1b@% z8jsUH8!%|1$#;v`B!s^H9xk|vOKn!0@srnI?lnYgW4Pb)C}Pe(fd8=7*Wi^XRb=P( zXP4xJKR=$C@jmmvlehrxLH3I?)zjP?Jpbs#;{O8eA`EW)e)71Tlh3;B1i6K47#`CZ z+1oF%5cAjQnS6w5*mXlfF;kFGmxQMszQ?O>)q4SUP-DKc#WS4nTrQX8^|qRPu`JVWz9b$#vOmed75=!ERxVbP%&EkYod5-IV6zD zCnz)&DV{Ykyl73h!-+K-yf(J@(+y3>^9q z5*&(*%zf>e3k3f(u}~_mTtD1Rxa6Pv06;78Dq2Mq6Y4h)&Tt9MIWQP(B)D36Z@#3e(K zfhn(X3L4eySA4#+d)d?V97P`;_%$hr9iOYk**gTTEuw=I60ETNwm+~BMv8a&*JpFF z*CiTOE7^>O*>CLteJ3rcG(arz@%<}@O7@0BCqmr>)@2a6Uz?5*+_!k}3uSO&U%cXa z0VUvbO8}N)Xl#a;@5d!pB>@%u@Ue|mnkN;Vh zK8%|}|I{HodUAYaXk>W2_T=4@Yf%kEhqiEc-nAeJz$4}_BY())SUXN3r zKN*ksMnV~WJ;hVcW7!|92ZgG%DZy&ZN0aqd_eEE@UbsgP@I!-#z~QlI5%>>SH`s{!n)p7~Pf0nyv5iV!6*%sHD-+^R@@Z+y< zrba!BXRhC4!i3vi710j@pNXWkg&N!{&OHAm>)hF)`)BRgg7myf-1q=@wHBYR&?AGF2DLU0>i!? z0@=nUC;O3k5A;v*Iqe1VhR1j1H>gX*OmTYs5WUtba+1Fu% z2Q?GZj$B)*^IoUOdrvS_W2B?z6O9Ru%U_@ zGq>Ztr4?1`EnLs$p!$IOt@VC@iLjmWM3QVq8zNP&ytumQpWyXpP>ki}_10zu71*P~ zPc1?AO0~QEZ$*pi%GkKLBqL{QSHRj}DI+m3H7J^{f2q^B7OeKP@j86UTI3-p(*AxK z8q!Q7#D&-OKKi}e+Z_sFuGIv!1=+ z-h8YeCbY27yj);rP8F0Jb<$A!Tc$!b^yz1z?3?j`^5zV1m7p&Gc-T)cpWs=!$iWQSSG&M>I0=n|R^wrQ!_O_{plX zPML<9hI(`Eltzdbx1j7UM{Io7T)DVvpjjR5M^&MN~!J- zr~*Q{4YtM87hpnzjcm8+${TzsRMZNGEg?WF<{;jJfB})p2)n^~=LZ&IRjGNfI~oxP z4p0CSl&SsmR7yc6@F-1-k*sFt6#*1fHf5K?)h1el^KNnhlToO+U-|<)1rrm>)gev` zO>RZ=x8&p(M}u1{6P<*>Y5m}efy5<#1rhr4#2BH|E^py)NAn3Bz|^@uzI~d^$EqZy zUVR#PrMUa@r`;hNju(HO@{ZrfvJaq-_T2^7pYM%ErN(-2EC>9QJjcANYj3Zv9Uhd( zMRQi!vt>{qp92F!G5OvWMHXZ!g~-TDsA^Ll3cT!mZ$q-8I;U&$O1}1Bw9I;-7gOZ&ooich3Hw-ZvVMi62_% z<6E!AhxnYJp^zLZ*}y*Pp0dY`JZ!^k^yi2D<1W|*c;*{p@+Rp(2MWpr0j>9gex(+uSkNGtcy5}JUBPFGRS5#3EVREf`WK1SL*lSi%w)|5I zy!FB2!Xp-@SIDW4l^Z%?hy=mN$O|k%%x`0k$ljy<%5MwxL!AE-lIK+AsHZhb$x5}S zY^UNUF_DtSn0{b)JQPSwdct2Gr8%`tYSXKLT4L3VF_T@aqPBC=>mL~(7rXnja6EVD zpU6d5V6_a0T1pnKC(yE8<)<`fV)8}{o%KM1hAN|KO>uy4m*mQcm`?=hJc4k!X2f!P z0^_?O@L6cC@zEqgQ0|9%c~+gWDhZR7RV^-MzNsnO+qdG|#huY7MA#&7OjfU^HA-ny z;nolJlnq}j(yJ9}YHDUSXG&DG`a(0mEGcjI~GemxDxrO>0#3H&8 zjZgph$+H!{Bi6wc3oCSe0<}5ngGkONh`wpUv;>MES-o9f)-W49oy&`FQghOCi7_65u0|vmO#v}O$;EHC=XPa>!9l)Mi zGTg@tRT&t7X`b`eq1(o!LI#FEkxiRZ^G1ozOq-fNsxY?ZGK=k782-5eD7X5(27A6>Bq)647NEss*dZi1eo%*;DgmMCZ|H4d zrX+@iOpA;6&{2Vf{d!kp$dMQXC5A1{ed+}L7KGPVyD+~OJ`)R?Jq2xMl=p_UYz2fD zRj{#lo>P!uVrTfh9G^6rh;DLVBG;r5$qhXv7=oFf)n9K^nt&YD; zOiWf5OZTQv2Z?^C3y`h_c?OWRWl(9$T1kAkGTp?hs8xBuP+Mw;+Ki76iHZ=h{ITgm z0)-g;K?LxQvz`~ikT2H4t`slb7W;||d8qU9*CaGif6RQ!`X;w{r64GXDc;lY1B8;S zorV>g?Gh1B7G%$7)nrtW`mWrxF}c|4qI+o*S6VK?)!Z7s<{~*`tVnU<-KdlZzS!~m zKqcG@cZD5x!_c2MPE?aiAN`ZxOP5yfrDuyFrr2k~^qT;UB9!Y!`TO7!?;H z<>F-zYISr@uDt(d4ux+G>+AK9*5NY!ke$NqWOoTPI*sRq^Q_Uc25$gAgYuf0B}!^~ zjHxnhJeF)u!OHYr(Kc~CGI)4NV2ifj*t6A<@dQP`kLY~h=VmvVx4*Bmg-CCF_s#O1 zMHZr1k@?^aa_Hx$Gx?zw9SZ^pxDT_Oy+_qS#SgC*-V`vgLa`}f&%Z9yP;%Jjba~TT zdW)hUnzOvGh=wBR(icII78t&%1Oj!01ka#09|DBCQ}x7$Ii&`99rTQ?Eb!viTYGEf zRFK@HDWNxs0_c~(-QC@My!DNwLTbdGE-47tluf)LmVZx%T%I$80|Gt(?)!qpAuBh; zX4Rp5U**I~oWf%O(#hA>nm)jN7gm*yD*VQ7VG%LR z)&Ftn!MBp>l9GZ(uA$IRm9ZyksBL+9Q3oWuVE_VEEmOuVopTzoy#D4Q$J;PxwX9fU zIfYp|>!cdduQfq#r*sNJg0s3JtJrd8j#W-mS)$0ir6EzN5t(O5I^3EXhG|5LjQ{#< zcRHbubzJDr_b4AQ;X&GS-@Fg*bL`q+MPfaM|y1{!JNCPso>lEepYf9l6>rp18E@A4ky1Ea`UN;M)xfk-~I7jm|X#BS=YyXc2bn|yV*F-b>soFMH(3< zE6Pr-_yi+^C_Qhn1KrBA<>KSi6i1IG4zyKHYBf`iEbYN8Ilj|h& zKsWkTV6@_Wk9auWfV>X>NleTSQ>SyS47qJL4h7vKaGKjru~c%Ut)iAe4;qBlh-gYn zx7=W1sZVYwDjnl`DD8w_=v(KagKMS#dAU8;%Ra1k)~JRvc!Ll`C6io#5u88JiC=z} zo>5S)T0nB;{dJ_!6<83$m(0ZBNAE90KwPEN_T$zeEU??%-QGqUzkr_4i{e*SvR3M_4o!CF2Dv*WkQ zP4?=-@R)$%2Vq}FYM}7yl^}vrj=IfrmX2L1mhBC9aBL;gi+i#{G}@NXP)ShO1zt}$ zNUvctp{58Ge<5aM1bcmFK^(2z8X+4W$#uZL!P^QJ6YSTzX^icS})@N~% zw{;HU=w=9-NlER~B4m>ASl6wH*tRb|j#=lL79I&*=4EcAgFMUlh!n)<=t~>43YC|( zCO6#NW<-05fzAlvSAM9DG%pRvMxwHnX;^80;>H#B8YKf%cIDQ-&59v%CHrWD#~Kk- zrmYu(wXi*L+mT=dYr(K@U|yd635Nvj_l~VUP7?MSLYrk$7P5=u?;nGP)ArmBL;Y}Q zp3_M^2B4-6MaHVx^;9#@>;S9G6n5soYBTCno7eRu)HWvD{p6Xt5jGj3j;gza;mphKT~4j zJvCNGmZZczml0yK_YD!+4~_1s1v3oNGrg-y(Rw>=wVK8ZE;DFnGNH)7ZMOb0O;I45 zk{Y*8y}wimhY|6!Sb+BEkwois;tftNy+sxM-uAb`U-s8`i@`E?@@_9upPAnpUKCJw z5E*VJ|BeyDIDpBTF090l=TWtsoS2wQ!NR3}@P?0yO2;PIT?-_nV$$P-h-xi27*H6Z z|7s~OUOi`g-r{IWQ)atlH)p13zj@;b3kOfhL8)!x{;`0efk{K%zZrh@8!e*W^W zC6B+(pi@~per2VdB1JvF(9PieTJR&|Ypbp*;u=l1dz_9`yT{`$K1bID{ue`11<%Q7zP(DJkteS?~g0>BXgq zan)Sh@4kP6{AIZU^=QEYo~rqTgwL@OY_va>S|et-oNUL=s~>u)ZLTC}GAd%Kj8}T? zoNr{p)-=cpnG59IgLW^hHFGFGO%w+ub{pqf6)M7gwdAzSu2ktL)dO{U+hNL^^aFen z2G-Vo2048cT(t1Pmj}RYNXR5OhlEG^PW7Bw!k~Ztn3TkLHaIt%1qo*w8y}1L_RSBu z&e`626}EhE>*TG-MN$F;0(Z3*Y@lymNKc287eQ@oYk-vCOycl#C^z4{Nt24U&N5#3 z!4V`5u!qwHKp&_W5P}GiT=9151snX$w!`&3$!6Sls{+;zhz=3iGry|j=wU9zwX_v= z&2dXTJ$m!Sy_dsPFXvmEN#xBN)x|nXMG{&`_-U6D`BumXWKb*H7J|&9I8GEx(q@oS~p#17~@-^v3TKzRkA7?m$q55gU8wD*{sGplA+IYEf_KqT_d5INj{8 zt{(+K5l}8=0}~zk>E$aStiG!whA%Po^v?wr3@_g}qSV-1z%1Rl*zYyg4+sE*J-9VY z4rE=ztX|GNfn45=Q zs&2Zct3UW883DNyUnYKN>u$6*?>=E=jVAlo{PLnxUQ8I+cs*JxA()$QM#;`;N3O@h zb=l_Sj*SfmrrN$WY`^l`N9S#V1VjIa>AOBjsP^p-dQ5&zpFx6xKyb$jB!q>z@ECt- z#r=~3?myVmz{G#$-wG47j%45H{23H(37JpS@RHu$Q8IG8*u;x3R2hC}!wcX!21^=m zDMzWTe^AW!-r9cu8skO^D8x(Ub-)>owLAJrNi0-skdqIJY5r8(qIF5_Hy(s5X5KHUlvNo5@$y*T0wJH2Q|C z?r?@Gh#B*(=%%NRt*VNn(r1dxdamXr$TO2)4!vsjyu?9pw(II>dlb*fA7cM9!9zem z&_3ObvtmHe@MRN6x78IM6KcuBrNU->xKOj7v_BMSd$NfPOd$pWK^jIeqkx#M1SKG> zt{TQB#(Zt6FEd&Q>j8VT^lB{q_Ak|;X?DGY4L4PgFcf{UYG`z_x?{9IcY{#HQGL8) z_FdoBz!$7UjBpyBW*=@_y{n+~bb|6OT`(dYODeA~?T{8F(;Mt91(Nq^w=d178;S-T zf`VXIW^vKK?2IC8SP#HJkqRDnRQEx>q6a5GSvEgl%={BzDNm&f^yqvNz{VaOW7RmWK~zqmWlQp7&B zVVUcV0uwe4j?Ujl3GJO61sg%nO{{ofzrM6x$STj86N!}yxK*=pxV)s==W&9GbqJOE zFx>rJLy5<;2OrdNbku%$ok;j9+mz-N@x^7A0Xmtnu?YpA5MhC>KsTtXdHy1x?L&8r zynLa#%}Xj6FY0{LIP4OpCpHws zB!zpwJSQwq4h-hY~g!cft_BG$@+ZqO>my$q1IdYM8r90L`V;&1I!@n=P zUR~a}BbY5Boc}a2J{|c@irUX>Lz-=JswEu;)J^Bh7NPEpWkX&uN1SICP>@aLQV%lF zFT0tmEaI_`#`5^w!V(j|%K1Xr}>`Nw2xvVC|c7M3gQ?|t2IW~X{w%O^-%Xj)Jh5r0F4@>vJ14bv}whBG;x&>5JrY#JDcGG91C(=iRO1tdX_~bCCCj{`= za2tHKX!HL(%AaH@84O4j6){*2KI5_4J?{3kV03%qC){PCF*+NJg=z()e^LcNA%e?V$3qjM z&jYeucFywS5$67_7gajEfH=+FkL~SA60FeH+0Qtin+92ayK%g!Z(o?3a=4i%1Tc}D zodUWbn#6A_?~IMRUgYN~?nu5D9r+bi_gO(y*F@(F0NmikMZTy~vt+pR9ktz~<67IC z^qkMI4Hj^tze2$lfxY*>XJnc8dQq89>PNY_{O7yIwEZ*;fG_5{<&3D*hN{eFQ43qP zT@SrtA>xz)l6^*3Gg{MLm+JB#>8P-~n%)K{RZ{$pK*Pj2fd>xtn2ny85_9=snFjBO zFVeDZ10y3fY(*!zYxXznyI4E>UtRbVu@1o5H(c<*F4;cd_rl+T7)(7b50rP=FR$vn zEwYIQlraS(3z8U~O<(m0J!twwPR{cJXzz-J`P)tJF*PF;eIo#C1-2)2WYduT?jYWVx1bTV7R zTuos3>}dv|JbR?o^i5P=0#D-$k>gb@QI$BT5;dV<{hl!enzU0eFkp80vf}#0Vy@5w zcGHQ3_-q}THAAo?YKd!kzR2kMyqb07rw40@cy_8wZkV&%BJZ>Qb+kEWRjN3Kh`aN+ z-0C%Zp5TD!7}4_twXO6H34nkQSCagOO{kbejA&3N7ZZbl{?VE1{&4A?AVQzOPFux} zZbNGoh@!#jA3h_cR?YLnr8LB@yt!7J_a=T3tNX{JOYKR7dC3uPgIQLo_#4{(pw_CA zJ?v#_Qy_57#-=7KhvSN#ZJY&6Pt&S&*Pv1>Uvd)bc=cn%K`LdDUulpLn* zuaQZcJtM7ujWpY;)mSQ`8CDXQ*$Qq^X7M?7cAkSG= zM^I2uiW&=~Lt#NGKfBeR%G%~&*skG=MYF%yi1gQTQdCvEfwJY( zr;k*8jKpU0#1idd!V*GNMrV@XI7&f55@*N(?y#-gvvmh~ZiuIdj+>N?H^mDOZw3Xo zUIC;Q0CG`)A{4(!u7!qr|Fl@F{2pid2?``>eYx333zsTlvxzMXE!CI3Hw)YMgKC{~D)ZaS&!$$k=LyOl-}7Bv>Tv(3^=Rt5Bsi*DBoM%V;a?e4a| zsBASW{AOgN-d|Z_CydktHnX+<>FK10^NoQ9{_vCx?9ioiHE>X_+KnNxV!`bLvqUeW z^4_#Y)PV^5+sjit?%@&a&=?9xR)|wAI5DO%?WEZpYsq%i(TKZ-Uy0Yt7cE7eX!^!K-596upW^!$?W>6C-WqS>N$@ zbRh*KEG|`3t#~z`8u{e7n465s7rPlK1yRbA=squEKn8G!GHBko2@`C~*qF_uGsLQwchZXqgkd zr)3)BBUB>)A^!ic_trsic3Zz-=Ou(BSO@`v2ZAKHyM#a>cyM=jhu{_mkSCJx3_w(#$uf2Tz)>8P?din|EFqHBqd|(@> zpD!-SW}WPX_o|6?m5Pb1wa@?5DZs&@|M0NIBOKT=>B4b`Ol&OgMMZh6wP0;m3SD*^ ztK1J!nnjj;g_+z|RhasVz(7-0G2jn>PmX^aacW|F+%G=g;EBX;F@dNC@lgnYch)dU z;frepWGDOjKG2+X5vgV)iPzD`()p`RReI5uccl;nZ;PQ7W`6tKzWM zmtGP}Z9N`ltKS zgG+aqb25+fW5!he4=6R(@yu|PA1Va9!olKzZH zp0Ycc6((w44JnQPOqVu|8F;D#aoG_>K)D(*84t{?cu6en0(s&Q(Ud5Z++|hR~lv`Lz>E! z`#v-X=)<a0RR(++%XG95nU66Q%+{DusulzI z1H7T+FW@BRs^u^wa@b^X+~H9z6lmzk%t1<2Wiby3r$muyS(6)ZeD3g|tK;WEpNWVb zfau6@i`MIxgaq%%u`)FJuD=`i*{<&qRJ)n;IaYaV~F2-*#XloVBQ zC+{l$9`JcZbaB>E#~U${t>@GauOtLTyl+P{vhuKqER;Lh;c;BsEB+NVNW;0Jl>MYL z=s7lgUp!rD!yYW2_0s|H_)EZS9n3a@d>G%_YPE});bYcJEW^pgG|J^co-z54KVL%f z-u9H{>4qRmOvBWjmfab1k}k?$EK5sD|4tR;fFM6~*--Rrgju(hnK^ts=`rb*;8EVv zLjJ77=NX!Ta*-}Pb!a-#sRSAeg9_Lh8|@E{A`fSs_t7*OBup2IUuz>HuddESgWM7c zK0batn-#7Id8EWEWk7F`=x@zWMz_{#@^b*_8lZ!K7aa2Ib=OL70BbH7UF+*Rw)*h% z`T4nAg);GclP58rOxnBYf@cVR!i`l9<{?jOsIk32YpJ#0AtDhx5vwAo8g7C@;*&If zeDtZw{aYi6f|J;Dvge+KruI)9r6vWK12AHu@=RTYT?ko$`7kK!cmF5MoKEGSIx_%&vG0nh)^}ufp$6IpVkXgieGu zqd;y)ahAdj0gyyMTPuz9%^%(5v!9?JKK>>`4l=`Xy05;u;*ZSzvPyh0N_EFdJl3E% z|2MewLTEEpCMNVq*Nyn$KvB*m@1>bid^zo78nUUzCgxIE8$QolfY)rI24%0!o+y9g zOtaYIIIO_`f;@EIE#g-1g5G@Tqo1A|g+@(3!jiN5AtIpoeC}0xI_)<$)#r0RF|nb< zim9=g6I9{=4mYW{F=lrz#w0LVn(4Pr}4@9MS}JR+RV-jkh(hpNM(;>DW~Oq0chC` zFFp>uqb1%2JQvfsTq@AyJfPxZ(0^fZpZ6@Dzlu`_bJ+h2gRDs9s#*Uf;B-sxwEBR? zV0&~@^64*mA%P1QX|mG0MPo8o1u4f zG*!WLu?kOvBJ3wUYAM(~t5l!>8-e-u<_xyQ>$w9R$U(}7Dv;^Tl zQOToo2t2s}1<%-r)9bV;{nbpyzY*jgL^8=KDUrP`uh4ad;@>7K>5rC|gMMNI)Sy#1a8u6slxZ<%4?%yka+ZGJ1Mxu7W?neEVg^s}f4U zFA92l{QGOW$o5|Xd`>In$KR~}I z;WGT+e?|N@JDl(zEn-H3!vCM!$izG`GcyB2)?1E;1ifF=(n@kAecf)Vw3<_9b2Rm_ zTk8b6-~-o9QM5!Bm-_|X{pA`=ekN^nQ2@y3kszN3+Qcy+&jmi0{!18%&1Un9Bp%~a zya$lWo16BmDppN(-u9&>9dOaF+bLgzgWpGxEQ}2Q^6k}G6AgdKWeb^?O$$_{!6_CE zI07>aJzxtprqEd#J1>nYOJ6d7!OlZLxQM!XqAd=&o9vIOcS3ra4bJF-bn0IzL-5vS zCknvK77?hXy!>-89V@We(=Eg9wO9pqhIePYi=pG*tfT6*o8j{r&xPg$pLA7*m=A z{ZEqq`in&GqA)?V+_0?oOd@7q*TNzM)N-dgr|{66f@pYEX(?0aO93K_$->SMJjNHK z6uo_=n75>lRYt@&tSmoeN#IP+&Zd6|iEW!vcfY%4$7Q^Kz-la|a2k3h`L?j2dw{To4t1c3s;nn!C)^5&W1X ze|tK#^!aKvz}Bu;4CG&xi&eu@cwHcn>x+YsjIQu@sIMT2z|+u4py4$&oM0T>x5{zFm>qNiuI2I|>Tn|N;NBS8f`0JKabUY9?`{S8yzkM#26liU zsW?W3?l8|%^9flH$9-0u9ZfSkv$FiFy>@@)n`jup6B7&iSFa!_7f>-x&E(#*sM&uU zuuyh)s4tjx+6Eje?}G(YK|!6BeOT|B59Ic8jRox`6C{Pl>HX_2(Rf}L48)^CpRlsB zh7xc;-Q2|ee7cDa#)1Zfd=kI_mDs{T7{9x_A4|qEwCATOXJ|LMJqzi8KtNG-ZM97V zrDR-NXQyZ_fYV`NnSKBM-6uyj?JtPH{bwdH9j@Sw0X_l2YKGSxljHWM-|Bz!To!0- z1@nN+JK+;H^19eC3 z?+W8VkuI?R>1OUswZkA zU%Opwhc~&y^7&*LwCXeMh$OFA?dPhCT(doSy-BP!Tf(ii7KV$h2p>M=`^&yTcCo>Q z5%Cr9&?Dg6fFD$h8UgrTAq4;ti3GjSK!H+|8>~}|)bnBFM+!|kOmA=R+Dhj@NedIi zhM?lnAvQ#{R`yknZKf`y#uy}(()`N7r1>#B7|;NuN)9NK9I!y3&;+s1QvX(l@*#kY%H_(;bBlnGzsw$ zdk}F#pOcxHsXJd=X1YBLiDS@`PoN&!ay#1&&&_=k;|h6(kN*uBj0f+oSvR<$flX%@ z)A8CaLt;FzA*q7ifj!YJ?+aq-UtDdLXGumzMWw$pN(;9p; zLoBTAV6H}S(D&qY^)Q8Zy3CVT(_ydn`eFy&#>OV*0MU`F$>!y;l}o*YuvOL6fPzHNkxBU@TcAt>d~zwNKb3h3rCxVDhQ`L} z7E{)xWo5tf6sVc)+C;tt1U&JUDUyhx(HoepGF_;zLSPiya$bw+??h#RJIJf0f<{-e z;IQAG^tpi=|8%v}&0M(*s!v}{dg>guj;{WEt95}g^~7L@L2B=POfR$0Ug+K_0sU=bCBHt3kB|Rp5|Ii-AJ^JpxSc^)>(o?^2pH`W64E|j=dgU} zMF6%SsJU4HD7@u9#+KHN`FEzE0xjE{A6=v7-Z%4)h~vR5dKe+6=qKG*7e zv|Q+SFaNG}JSGP#1cU*nX4MI~r>BRe#r^h@*>b-20WdVa8=K&LmJ$*Y0!Qnqho>jd zg{Y8P*Hfy_P<(&Dp6?5DP#S`9aQHjBPY?2y3fj86Kb2O&llk41%Z4DIs?3$XcIeMH zd!nYvCVxe`fuX7h85>i@(y4`_6MFfUMPh*(u%p!u)czD+ln4@jFbxc$9v5|idXyXN zd^DCV;}0IJ+Im4w|!@t8jLlO-6Aj~j-mZD7|+<_6dkgBalrc+71bZq!<7(nu7Ziy+haMH zgQ@&MIvkd*W-!*5T&B+u5`cKF2Aq2O$d8%1kL9UwLqMq-4d`Yj!lS?92g)qmIV-K! z{1{Sr*9KE~w-=juy)N~b!P8WF#e$sh%64S?M@h-{TD!EB>0|FQ>jk%DLGK4(ETAul z3#qFU#4^IMhSCK6L36ml6u}5oJjUN(gI2)G zK`qS|J8X0UwpMW5miPAj3E0h-yGIOaS2w~WMSHP!ak#uZm^afri!@%oaG zu)U3(`uhVR)aM(5dKTZndwj0f58W^J1K{mqs?GY8#aa!4;9$Jd(IJ(Lqi@Gyv`OZ7 zAxTwfJd$hi0@K}FZIZku@?p=UfpvtCmGQiDJg~L5mtS{?V{rc|om7pW!#&bXWNd68 z3g)ft-Xy(Zp(+*%CUGYtA;)rw=Z)R&nb~-W4k=`LZOz|F5FXm!FXeGQVW`>UOl&fg z(gk)UlvMD6n_K>buS!!>nf-+9k^6E4F|Tvm^t7r`^Kgb(1e4`76Jl2aLgH*s#?8iy zwNQYxu)Q26Q;nky((*p+jLPbRK%`TWZO^9dlDXY6tY#~5tT7-*fYx+yy9E8T5@74~ z&p03mTGE_|ScK(LGkS@3le`)uJ39^s$A;ZbIm%#4i_dGZh+n`41I=OlSxp9W=@6|n z&jj=U(~SNy5JP*(^$gtmXs@Kje4n8k5Phvn)(1e!JJ@JxX)B%W9zaAyMc&Y-WEkYg z*g7~MK~&Y%(`D0|-&yOrj&F|uf$j%#@i!L-dRA6pKYkceP^>FXYd3p5!NtXW$$Q8O zBoYJy##q)nA3TB_o%UU=0t;P0ikX@D@slS)nwmu5Z`o1_!D&-PVpk^{zVlDrfH?zz z#7zAM1HY%HP_VGLwQPes^trg$z5sYN;I{C&8f!!>e$2!p<1=os2zjN&6h@QV#TQWP z4HQW5gngylTIZ$lrc_7sBOu~C!w8vJS=&dA`d-A?Kt8p2d7kY~XzZ=Mc*GiQ?^JqX}qYlhfkRP=yfzGPrW0btzj=7Ro1!o*;4 z)T)1yLXHgi+qX{^8epw>I$n9xsgaRbff&U3rfRgD_UiynwxB@?AC#pvF+n3?5h?Si zGM`{F8wHI{fXc(d!=qNs&?O;>!vaiAC7r0-KV3$1!?zkO`>8;gzR~UcDGa)xpf)r; z{W6+bR{3oFQTW` z^^Vu5TPG)RIc*k2YKpa(^)H#`YrVAOj&Z=Vfhz(+WVoI0(%gW)4$JdATLw9`wOldG z?x+Pa2eYMv#2M%iKR@3YP|&CT=ECAVYgH8%2&3N-6j%%}2|_TPzj1mnonSZl?Y zZEkMnYQNsPrl5+^ewVZ@tTcUAB#n@gmb$vHIIO3A%oM&4Q;vouCpTWGhP^il_ZvGj8E$fSfwTg>b94jM2%dW;&OReqZBUM4p=ntjE7SCG{HXqKQ)Cfke(A zP@o|*nfu`s*EO&wUoPVj2vmHJN(817U5xAO=X*MnTkS5$(0J)OLVBG~I8WZCPQ zZL!hywPvBJ{+hm0XMfB`3e{3wvWxq3JRZmGuRx4iE?lDkPaR26M<>Qfl6L8ZP zzJWnxR60+!jogn(0mM+5f7ty_dy~YTg4d5rbXq!77aC3<>SB`IqL7i1A*>U=lFxcj zotvZfRZ7#b?66*Y%3qCP=yY^+5Xfs$4?j{|#vagpyarU2=4!wWx2N;47ytax?SpE3 zkB!YfulO_1XMzu$ZVwJGK7@7+BlHePNWcg206&cy>-6_m0C!i@S}?5g^_k-gRdmmb z7=2*6GO^tn{T}idQ%tk)y7+okf9)Ao z>R3v(rj!&K_z)Rxh+IkWH8c0O-0i1H%~d_^et#j^?N7_aCnoAcb-Y)Am{+ZErU#nl zDJ$zltZIPVv?nFG+Z>Rt>TEU-Ai#Eg%?rw&??dp0lF6|m_636B z2dxT*b6wLjpeWePMqeb=So+4nlpO6G+JVP(dof4#s)h~~0*ZP%2%JTL7))L7iSYFF zB#V)dtCENz_ncP95S4!A;-RCbGG%9H_xb9C67nV{m97ZVwX+=sbx4-3VN=Vr z3c_!TURH1d$TlVk-zP^W^=H8G0=A_P5T8L9#C{DoW*(&>o-9cX7-o@&f|H!`qa; z=EncyJOC;8JvB8I+qA0x2^|QJCG**lFEJ$QGk#2B=W|o(AZ>x7W z!Icr#2#|a204I2Sd*w|!29$KV_*yc_49IrA0(sL07t-y$y|vq`(`ne0PVhk%Z~>q# za8W6#Q2o2~qgoIA#X2Jc2ylW)E)JGZAls{(u{lXQ!0{8o8|IbdRZ#)7SkABEbcWt5 z7^T4uXZ*!5Nz;`NW=&`Evuu?>;5i-hIZ`7u4{U?p^%=f-!>4GK=ELbSVl5t*_b_WM zX6t>`@j@lZTGO&`9r_;Zu21t-N{U}%juc6noNX_ngNV%W9c+n=?oF#c1Y#f~E+V8z zFMX%#2b8&sb#M8gRJfRaLjm`@dl#TKa} zE|2h?vK*)ppR&@ZX)Al?E4Hx&swISmK%BWwLCo=R9Lryj2p_O!w*0N)2xy!0vuT123mDvVHXKYrwKj}<;+>SXdOe3*K*aXXg2#3GPWg3fah3RRn-Bm%6z#dw7pnF%j0Ys!D*u}SC)P~iF6!{f61m1Lpem*kcV*XJWr;AQB6;2qGeSjF!})a&i;c+{&+i&?T=KzORwaYz2w zXbp#Je3&%%kCIOhzyW`Gd;AKq>nS|WtxY$u@ocy{yM+)PZaeT*n%vJt;9jIFBN;($ z9X{miz@ExNur8LX1WDynK47W0R($uN-xu42J@QePbWaoptK_DB=qY(sT>wUx$cB@07vNlR)V zLkchAvY{mG&w+0-4F2SCCMVdzauPwQ)8Z8tfr9=;BX02M?CjG_xMq7G`f*2`&R-Bj z9AELmbu3#)&)hs1xQgw-cKx^0%C)&(1qRkuF^{ae7&|W-=;yCL+{3;r-bb0U+z#aaxG~Y7%&Es zIJ-J^ygN9T`W4Y^5J4eQFV#X*WGrIhFc2j$7_&)|%MeBXow_A^*#JoVQ9|NQ17F$U3TDo`T=A7ySsLQazUV8d(hf;nSrgdBXUW zOSJv3(FtBLGh-nb>RaAh;L!x}yTkqbg@rp#oOZ{5gMPkpAe02capXKa1f<@4FTsBJ z`O3#l^@fIpmAMV5pY6=y$;ruqRtF+?kI;|iY?4NbG&omQMHJry0g2k%zQ0eScfLDe zI$Qa6baWK3b%229_3E5Wi$c=)zC1Q ztt=TSRAc>s*A0r0foXZU9xdz8t5RGXyfIg4=1&^O7zDf}1XhtW&Bsi~JM0^_V9>|R zN-GqeMooRM;)L6lfvIYE4U{U7Nq-6L&JGusg3 z0Sb-;Nl;*8B+7|~5wbqzbw0qAVQ_-(y1exUk*K!nB8@V`9eJ-jkg&=0zrDL-zOS%@ z_*l)kyvYB+2EL8e=! zK|v{0Yltm!`S*bm+y0D7WM)BaKC>0(a zUhB0+RlHRD1Xu(smWXsEjX`XQ>bIbXze+tiB>piv-mTA(s&%e9h4!A?cDV@I6(m%S zhJ2As=LVJjGfA5AtOra+qx*5^}>CFHTtzRj8ZUVTY`aqM}{Osj_PoqcNjXb@k z?cc^`JJI}e%m-qD;B`iZ_2{!xc8KxcAO7b*>6pa-yHC>iUz#=l@)4NfU$8@7zhxHU z`u8LA_3Ou+ganPp)J>oR`yc=_4UhGiHE68d-ZZfN>Pm^d|WydbXm!-!2<#e3<5|7a&@-ap(A(1;7f($U=hVz z4pGBp@%$G%5-JVJB43oV_51TJ9}|B2=!)}ij|$iTDLEwtn94tM;>Mg+O#W(YfX@T? z0U8=U2O7vcw#`s70A7Lv0|#^zNI^d_?)5p>GrOb(ow8mlKz+c@ca(gTyi#KpuZC)yZUON z|Gl%Z@t}@q_GnAR_ms$Q=Ffiye0v-?!wnCY9%wD!I&K^Im*heGB8UGQq};dPJJPI< zvG^9!b(d#;H@k#BbjP-|P{dF={}q2ok{ViA&PV6aZy|M=CMa@xM|Q3ASo`T#+}F>A zT5FgiX)A5`Jn!OQDPMD6G{$U+atWkBBLYn>BVS<2iJv6TLC}efe=BhLk2kQPq7y&T z6DzfS1cq|FA^R&0eodq$EB0E8t0eSWGagz_7~>KA;*hsLf7gTXh;{rIr%)m~YDX}# z#r{`*(XY(<&%?X~qo)mQ%Jjtlh+v0h>xQILnLlqxpMI{PFNDm7^$=O;ff-9bnnJVS zBil_#Fjk-V;Z6NTK*i*&dm2dVJ>Htx)7pYJl$Y+cXbkM%-nw9y&(M#tR+VBx?ondK zC%t0E8Y77Ao1p90gz!((w{kSsw(qp2O_{?TAm??~lwpAWY z8hajVGcKnkn1a`mEB<|I4H!HGRdUn@38r}#a4Fm|_hX%&c6k~*I9wgQ<*t*=8TYEO&@=_9IjNe((d2M7VcZ1T$a=K z3uAhHpA)1rwbqPs&I+sOA;_93(P=2WP$T(Jkdl@JO^q8di}K{8lfDTyj7Fa`s%o=K zo0-482*+ftlkL*1lnZGaG-^CK8`nIx7BX0ia8;;&kii(8~4Vq2K&pzfRxsJWr3T!(9gQo z*_DQKjYl zn>oJv{p2i3j>D)Dm}#`l?OjSEGd#n`Wu1e+TUy@Y7&)_4D#^;l^^;ZL{UVo9T9yq)9&7gds? zWpt?B8Li#-eoc+-9q-~aW~*`EtnSpAdW||I6(?Mr%k)8QCV8-BwJmUvC;Ge(AduUL zywo^q#ye*(+tV+Z1NAd*UH3DVAt~k7Ga;_lC!tcVddAL;n}H>c&o1j8Hddr{mkLMp zc7GF3(w;NSq37r`o-2wAj!PT(lb~=i+Mi;7fF9%>{s3A#@4Am%=d!m_cR#G;f%|~b zLnxoc>r7+DTG42e?QIWtLs2ZM^PxP~V+ce!ugwS6V(W&4qckBowOCYCByDA`P+=

ShE}J?_OEFSCE%9F0W$Hn0}PAN6W@z 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 literal 0 HcmV?d00001 diff --git a/docs/local_environment_steps.md b/docs/local_environment_steps.md index 1479c77..ad38329 100644 --- a/docs/local_environment_steps.md +++ b/docs/local_environment_steps.md @@ -34,9 +34,12 @@ Optionally set environment variables via the following commands: azd env set 'AZURE_VM_ADMIN_PASSWORD' '' ``` +Optionally you can use the existing log analyitcs workspace if required. + +Follow this guide to set the existing log analytics workspace [Existing Workspace ID](/docs/re-use-log-analytics.md) + # Deploy -> ⚠️ **Note:** The latest version of the Azure Developer CLI (AZD) is currently limited on prompting for missing parameters. The feature flag parameters in this solution have been temporarily defaulted to `'disabled'` until this limitation is lifted and prompting will resume. To provision the necessary Azure resources and deploy the application, run the azd up command: ```powershell diff --git a/docs/re-use-log-analytics.md b/docs/re-use-log-analytics.md new file mode 100644 index 0000000..9ca1d91 --- /dev/null +++ b/docs/re-use-log-analytics.md @@ -0,0 +1,31 @@ +[← Back to *DEPLOYMENT* guide](local_environment_steps.md#deploy) + +# Reusing an Existing Log Analytics Workspace +To configure your environment to use an existing Log Analytics Workspace, follow these steps: +--- +### 1. Go to Azure Portal +Go to https://portal.azure.com + +### 2. Search for Log Analytics +In the search bar at the top, type "Log Analytics workspaces" and click on it and click on the workspace you want to use. + +![alt text](./images/re_use_log/logAnalyticsList.png) + +### 3. Copy Resource ID +In the Overview pane, Click on JSON View + +![alt text](./images/re_use_log/logAnalytics.png) + +Copy Resource ID that is your Workspace ID + +![alt text](./images/re_use_log/logAnalyticsJson.png) + +### 4. Set the Workspace ID in Your Environment +Run the following command in your terminal +```bash +azd env set AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID '' +``` +Replace `` with the value obtained from Step 3. + +### 5. Continue Deployment +Proceed with the next steps in the [deployment guide](local_environment_steps.md#deploy). diff --git a/infra/main.bicep b/infra/main.bicep index 90c9a9a..86b5415 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -114,6 +114,14 @@ param authClientId string? @description('Client secret for registered application in Entra for use with app authentication.') param authClientSecret string? +@description('Optional: Existing Log Analytics Workspace Resource ID') +param existingLogAnalyticsWorkspaceId string = '' + +var useExistingLogAnalytics = !empty(existingLogAnalyticsWorkspaceId) +var existingLawSubscription = useExistingLogAnalytics ? split(existingLogAnalyticsWorkspaceId, '/')[2] : '' +var existingLawResourceGroup = useExistingLogAnalytics ? split(existingLogAnalyticsWorkspaceId, '/')[4] : '' +var existingLawName = useExistingLogAnalytics ? split(existingLogAnalyticsWorkspaceId, '/')[8] : '' + var defaultTags = { 'azd-env-name': name } @@ -134,7 +142,12 @@ module appIdentity 'br/public:avm/res/managed-identity/user-assigned-identity:0. } } -module logAnalyticsWorkspace 'br/public:avm/res/operational-insights/workspace:0.11.0' = { +resource existingLogAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2023-09-01' existing = if (useExistingLogAnalytics) { + name: existingLawName + scope: resourceGroup(existingLawSubscription, existingLawResourceGroup) +} + +module logAnalyticsWorkspace 'br/public:avm/res/operational-insights/workspace:0.11.0' = if (!useExistingLogAnalytics) { name: take('${name}-log-analytics-deployment', 64) params: { name: toLower('log-${name}') @@ -145,13 +158,15 @@ module logAnalyticsWorkspace 'br/public:avm/res/operational-insights/workspace:0 } } +var logAnalyticsWorkspaceResourceId = useExistingLogAnalytics ? existingLogAnalyticsWorkspace.id : logAnalyticsWorkspace.outputs.resourceId + module applicationInsights 'br/public:avm/res/insights/component:0.6.0' = { name: take('${name}-app-insights-deployment', 64) params: { name: toLower('appi-${name}') location: location tags: allTags - workspaceResourceId: logAnalyticsWorkspace.outputs.resourceId + workspaceResourceId: logAnalyticsWorkspaceResourceId } } @@ -160,7 +175,7 @@ module network 'modules/virtualNetwork.bicep' = if (networkIsolation) { params: { resourceToken: resourceToken allowedIpAddress: allowedIpAddress - logAnalyticsWorkspaceId: logAnalyticsWorkspace.outputs.resourceId + logAnalyticsWorkspaceId: logAnalyticsWorkspaceResourceId location: location tags: allTags } @@ -174,7 +189,7 @@ module keyvault 'modules/keyvault.bicep' = { networkIsolation: networkIsolation virtualNetworkResourceId: networkIsolation ? network.outputs.resourceId : '' virtualNetworkSubnetResourceId: networkIsolation ? network.outputs.defaultSubnetResourceId : '' - logAnalyticsWorkspaceResourceId: logAnalyticsWorkspace.outputs.resourceId + logAnalyticsWorkspaceResourceId: logAnalyticsWorkspaceResourceId roleAssignments: concat(empty(userObjectId) ? [] : [ { principalId: userObjectId @@ -206,7 +221,7 @@ module containerRegistry 'modules/containerRegistry.bicep' = if (acrEnabled) { networkIsolation: networkIsolation virtualNetworkResourceId: networkIsolation ? network.outputs.resourceId : '' virtualNetworkSubnetResourceId: networkIsolation ? network.outputs.defaultSubnetResourceId : '' - logAnalyticsWorkspaceResourceId: logAnalyticsWorkspace.outputs.resourceId + logAnalyticsWorkspaceResourceId: logAnalyticsWorkspaceResourceId tags: allTags } } @@ -219,7 +234,7 @@ module storageAccount 'modules/storageAccount.bicep' = { networkIsolation: networkIsolation virtualNetworkResourceId: networkIsolation ? network.outputs.resourceId : '' virtualNetworkSubnetResourceId: networkIsolation ? network.outputs.defaultSubnetResourceId : '' - logAnalyticsWorkspaceResourceId: logAnalyticsWorkspace.outputs.resourceId + logAnalyticsWorkspaceResourceId: logAnalyticsWorkspaceResourceId roleAssignments: concat(empty(userObjectId) ? [] : [ { principalId: userObjectId @@ -254,7 +269,7 @@ module cognitiveServices 'modules/cognitive-services/main.bicep' = { virtualNetworkResourceId: networkIsolation ? network.outputs.resourceId : '' virtualNetworkSubnetResourceId: networkIsolation ? network.outputs.defaultSubnetResourceId : '' principalIds: deploySampleApp ? [appIdentity.outputs.principalId] : [] - logAnalyticsWorkspaceResourceId: logAnalyticsWorkspace.outputs.resourceId + logAnalyticsWorkspaceResourceId: logAnalyticsWorkspaceResourceId aiModelDeployments: [ for model in [aiEmbeddingModelDeployment, aiGPTModelDeployment]: { name: empty(model.?name) ? model.modelName : model.?name @@ -303,7 +318,7 @@ module aiSearch 'modules/aisearch.bicep' = if (searchEnabled) { networkIsolation: networkIsolation virtualNetworkResourceId: networkIsolation ? network.outputs.resourceId : '' virtualNetworkSubnetResourceId: networkIsolation ? network.outputs.defaultSubnetResourceId : '' - logAnalyticsWorkspaceResourceId: logAnalyticsWorkspace.outputs.resourceId + logAnalyticsWorkspaceResourceId: logAnalyticsWorkspaceResourceId roleAssignments: union(empty(userObjectId) ? [] : [ { principalId: userObjectId @@ -354,9 +369,10 @@ module virtualMachine './modules/virtualMachine.bicep' = if (networkIsolation) enableAcceleratedNetworking: true enableMicrosoftEntraIdAuth: true userObjectId: userObjectId - workspaceId: logAnalyticsWorkspace.outputs.resourceId + workspaceId: logAnalyticsWorkspaceResourceId location: location tags: allTags + dcrLocation: useExistingLogAnalytics ? existingLogAnalyticsWorkspace.location : logAnalyticsWorkspace.outputs.location } dependsOn: networkIsolation ? [storageAccount] : [] } @@ -370,7 +386,7 @@ module apim 'modules/apim.bicep' = if (apiManagementEnabled) { publisherName: '${name} API Management' sku: 'Developer' networkIsolation: networkIsolation - logAnalyticsWorkspaceResourceId: logAnalyticsWorkspace.outputs.resourceId + logAnalyticsWorkspaceResourceId: logAnalyticsWorkspaceResourceId virtualNetworkResourceId: networkIsolation ? network.outputs.resourceId : '' virtualNetworkSubnetResourceId: networkIsolation ? network.outputs.defaultSubnetResourceId : '' tags: allTags @@ -385,7 +401,7 @@ module cosmosDb 'modules/cosmosDb.bicep' = if (cosmosDbEnabled) { networkIsolation: networkIsolation virtualNetworkResourceId: networkIsolation ? network.outputs.resourceId : '' virtualNetworkSubnetResourceId: networkIsolation ? network.outputs.defaultSubnetResourceId : '' - logAnalyticsWorkspaceResourceId: logAnalyticsWorkspace.outputs.resourceId + logAnalyticsWorkspaceResourceId: logAnalyticsWorkspaceResourceId databases: cosmosDatabases sqlRoleAssignmentsPrincipalIds: deploySampleApp ? [appIdentity.outputs.principalId] : [] tags: allTags @@ -415,7 +431,7 @@ module appService 'modules/appservice.bicep' = if (deploySampleApp) { userAssignedIdentityName: appIdentity.outputs.name appInsightsName: applicationInsights.outputs.name keyVaultName: keyvault.outputs.name - logAnalyticsWorkspaceResourceId: logAnalyticsWorkspace.outputs.resourceId + logAnalyticsWorkspaceResourceId: logAnalyticsWorkspaceResourceId skuName: 'B3' skuCapacity: 1 imagePath: 'sampleappaoaichatgpt.azurecr.io/sample-app-aoai-chatgpt' @@ -475,7 +491,7 @@ output AZURE_VM_RESOURCE_ID string = networkIsolation ? virtualMachine.outputs.i output AZURE_VM_USERNAME string = servicesUsername output AZURE_APP_INSIGHTS_NAME string = applicationInsights.outputs.name output AZURE_CONTAINER_REGISTRY_NAME string = acrEnabled ? containerRegistry.outputs.name : '' -output AZURE_LOG_ANALYTICS_WORKSPACE_NAME string = logAnalyticsWorkspace.outputs.name +output AZURE_LOG_ANALYTICS_WORKSPACE_NAME string = useExistingLogAnalytics ? existingLogAnalyticsWorkspace.name : logAnalyticsWorkspace.outputs.name output AZURE_STORAGE_ACCOUNT_NAME string = storageAccount.outputs.storageName output AZURE_API_MANAGEMENT_NAME string = apiManagementEnabled ? apim.outputs.name : '' output AZURE_VIRTUAL_NETWORK_NAME string = networkIsolation ? network.outputs.name : '' diff --git a/infra/main.json b/infra/main.json index d648f4e..86799e2 100644 --- a/infra/main.json +++ b/infra/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "12092550075038962203" + "version": "0.36.177.2456", + "templateHash": "4954886504367674411" } }, "definitions": { @@ -1024,9 +1024,20 @@ "metadata": { "description": "Client secret for registered application in Entra for use with app authentication." } + }, + "existingLogAnalyticsWorkspaceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional: Existing Log Analytics Workspace Resource ID" + } } }, "variables": { + "useExistingLogAnalytics": "[not(empty(parameters('existingLogAnalyticsWorkspaceId')))]", + "existingLawSubscription": "[if(variables('useExistingLogAnalytics'), split(parameters('existingLogAnalyticsWorkspaceId'), '/')[2], '')]", + "existingLawResourceGroup": "[if(variables('useExistingLogAnalytics'), split(parameters('existingLogAnalyticsWorkspaceId'), '/')[4], '')]", + "existingLawName": "[if(variables('useExistingLogAnalytics'), split(parameters('existingLogAnalyticsWorkspaceId'), '/')[8], '')]", "defaultTags": { "azd-env-name": "[parameters('name')]" }, @@ -1037,6 +1048,15 @@ "authClientSecretName": "auth-client-secret" }, "resources": { + "existingLogAnalyticsWorkspace": { + "condition": "[variables('useExistingLogAnalytics')]", + "existing": true, + "type": "Microsoft.OperationalInsights/workspaces", + "apiVersion": "2023-09-01", + "subscriptionId": "[variables('existingLawSubscription')]", + "resourceGroup": "[variables('existingLawResourceGroup')]", + "name": "[variables('existingLawName')]" + }, "appIdentity": { "condition": "[variables('deploySampleApp')]", "type": "Microsoft.Resources/deployments", @@ -1518,6 +1538,7 @@ } }, "logAnalyticsWorkspace": { + "condition": "[not(variables('useExistingLogAnalytics'))]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[take(format('{0}-log-analytics-deployment', parameters('name')), 64)]", @@ -4560,9 +4581,7 @@ "tags": { "value": "[variables('allTags')]" }, - "workspaceResourceId": { - "value": "[reference('logAnalyticsWorkspace').outputs.resourceId.value]" - } + "workspaceResourceId": "[if(variables('useExistingLogAnalytics'), createObject('value', extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', variables('existingLawSubscription'), variables('existingLawResourceGroup')), 'Microsoft.OperationalInsights/workspaces', variables('existingLawName'))), createObject('value', reference('logAnalyticsWorkspace').outputs.resourceId.value))]" }, "template": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", @@ -5274,9 +5293,7 @@ "allowedIpAddress": { "value": "[parameters('allowedIpAddress')]" }, - "logAnalyticsWorkspaceId": { - "value": "[reference('logAnalyticsWorkspace').outputs.resourceId.value]" - }, + "logAnalyticsWorkspaceId": "[if(variables('useExistingLogAnalytics'), createObject('value', extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', variables('existingLawSubscription'), variables('existingLawResourceGroup')), 'Microsoft.OperationalInsights/workspaces', variables('existingLawName'))), createObject('value', reference('logAnalyticsWorkspace').outputs.resourceId.value))]", "location": { "value": "[parameters('location')]" }, @@ -5290,8 +5307,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "14803697760422194567" + "version": "0.36.177.2456", + "templateHash": "4627109933372413847" } }, "parameters": { @@ -10526,9 +10543,7 @@ }, "virtualNetworkResourceId": "[if(parameters('networkIsolation'), createObject('value', reference('network').outputs.resourceId.value), createObject('value', ''))]", "virtualNetworkSubnetResourceId": "[if(parameters('networkIsolation'), createObject('value', reference('network').outputs.defaultSubnetResourceId.value), createObject('value', ''))]", - "logAnalyticsWorkspaceResourceId": { - "value": "[reference('logAnalyticsWorkspace').outputs.resourceId.value]" - }, + "logAnalyticsWorkspaceResourceId": "[if(variables('useExistingLogAnalytics'), createObject('value', extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', variables('existingLawSubscription'), variables('existingLawResourceGroup')), 'Microsoft.OperationalInsights/workspaces', variables('existingLawName'))), createObject('value', reference('logAnalyticsWorkspace').outputs.resourceId.value))]", "roleAssignments": { "value": "[concat(if(empty(parameters('userObjectId')), createArray(), createArray(createObject('principalId', parameters('userObjectId'), 'principalType', 'User', 'roleDefinitionIdOrName', 'Key Vault Secrets User'))), if(variables('deploySampleApp'), createArray(createObject('principalId', reference('appIdentity').outputs.principalId.value, 'principalType', 'ServicePrincipal', 'roleDefinitionIdOrName', 'Key Vault Secrets User')), createArray()))]" }, @@ -10544,8 +10559,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "8370014477286310434" + "version": "0.36.177.2456", + "templateHash": "8809412115733006491" } }, "definitions": { @@ -17113,9 +17128,7 @@ }, "virtualNetworkResourceId": "[if(parameters('networkIsolation'), createObject('value', reference('network').outputs.resourceId.value), createObject('value', ''))]", "virtualNetworkSubnetResourceId": "[if(parameters('networkIsolation'), createObject('value', reference('network').outputs.defaultSubnetResourceId.value), createObject('value', ''))]", - "logAnalyticsWorkspaceResourceId": { - "value": "[reference('logAnalyticsWorkspace').outputs.resourceId.value]" - }, + "logAnalyticsWorkspaceResourceId": "[if(variables('useExistingLogAnalytics'), createObject('value', extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', variables('existingLawSubscription'), variables('existingLawResourceGroup')), 'Microsoft.OperationalInsights/workspaces', variables('existingLawName'))), createObject('value', reference('logAnalyticsWorkspace').outputs.resourceId.value))]", "tags": { "value": "[variables('allTags')]" } @@ -17126,8 +17139,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "2158520837294746606" + "version": "0.36.177.2456", + "templateHash": "16324990370746729243" } }, "parameters": { @@ -23337,9 +23350,7 @@ }, "virtualNetworkResourceId": "[if(parameters('networkIsolation'), createObject('value', reference('network').outputs.resourceId.value), createObject('value', ''))]", "virtualNetworkSubnetResourceId": "[if(parameters('networkIsolation'), createObject('value', reference('network').outputs.defaultSubnetResourceId.value), createObject('value', ''))]", - "logAnalyticsWorkspaceResourceId": { - "value": "[reference('logAnalyticsWorkspace').outputs.resourceId.value]" - }, + "logAnalyticsWorkspaceResourceId": "[if(variables('useExistingLogAnalytics'), createObject('value', extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', variables('existingLawSubscription'), variables('existingLawResourceGroup')), 'Microsoft.OperationalInsights/workspaces', variables('existingLawName'))), createObject('value', reference('logAnalyticsWorkspace').outputs.resourceId.value))]", "roleAssignments": { "value": "[concat(if(empty(parameters('userObjectId')), createArray(), createArray(createObject('principalId', parameters('userObjectId'), 'principalType', 'User', 'roleDefinitionIdOrName', 'Storage Blob Data Contributor'))), createArray(createObject('principalId', reference('cognitiveServices').outputs.aiServicesSystemAssignedMIPrincipalId.value, 'principalType', 'ServicePrincipal', 'roleDefinitionIdOrName', 'Storage Blob Data Contributor')), if(parameters('searchEnabled'), createArray(createObject('principalId', if(parameters('searchEnabled'), reference('aiSearch').outputs.systemAssignedMIPrincipalId.value, ''), 'principalType', 'ServicePrincipal', 'roleDefinitionIdOrName', 'Storage Blob Data Contributor')), createArray()))]" }, @@ -23354,8 +23365,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "17337127158083160925" + "version": "0.36.177.2456", + "templateHash": "17067067292062172355" } }, "definitions": { @@ -35346,9 +35357,7 @@ "virtualNetworkResourceId": "[if(parameters('networkIsolation'), createObject('value', reference('network').outputs.resourceId.value), createObject('value', ''))]", "virtualNetworkSubnetResourceId": "[if(parameters('networkIsolation'), createObject('value', reference('network').outputs.defaultSubnetResourceId.value), createObject('value', ''))]", "principalIds": "[if(variables('deploySampleApp'), createObject('value', createArray(reference('appIdentity').outputs.principalId.value)), createObject('value', createArray()))]", - "logAnalyticsWorkspaceResourceId": { - "value": "[reference('logAnalyticsWorkspace').outputs.resourceId.value]" - }, + "logAnalyticsWorkspaceResourceId": "[if(variables('useExistingLogAnalytics'), createObject('value', extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', variables('existingLawSubscription'), variables('existingLawResourceGroup')), 'Microsoft.OperationalInsights/workspaces', variables('existingLawName'))), createObject('value', reference('logAnalyticsWorkspace').outputs.resourceId.value))]", "aiModelDeployments": { "copy": [ { @@ -35390,8 +35399,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "15409590568992705867" + "version": "0.36.177.2456", + "templateHash": "17900998747221223162" } }, "definitions": { @@ -41768,8 +41777,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "2348080591288311162" + "version": "0.36.177.2456", + "templateHash": "2570220912122887221" } }, "definitions": { @@ -44512,8 +44521,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "2348080591288311162" + "version": "0.36.177.2456", + "templateHash": "2570220912122887221" } }, "definitions": { @@ -47258,8 +47267,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "2348080591288311162" + "version": "0.36.177.2456", + "templateHash": "2570220912122887221" } }, "definitions": { @@ -50004,8 +50013,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "2348080591288311162" + "version": "0.36.177.2456", + "templateHash": "2570220912122887221" } }, "definitions": { @@ -52747,8 +52756,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "2348080591288311162" + "version": "0.36.177.2456", + "templateHash": "2570220912122887221" } }, "definitions": { @@ -55493,8 +55502,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "2348080591288311162" + "version": "0.36.177.2456", + "templateHash": "2570220912122887221" } }, "definitions": { @@ -58236,8 +58245,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "2348080591288311162" + "version": "0.36.177.2456", + "templateHash": "2570220912122887221" } }, "definitions": { @@ -61002,8 +61011,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "15578119539506362308" + "version": "0.36.177.2456", + "templateHash": "1804217404312710827" } }, "parameters": { @@ -61188,9 +61197,7 @@ }, "virtualNetworkResourceId": "[if(parameters('networkIsolation'), createObject('value', reference('network').outputs.resourceId.value), createObject('value', ''))]", "virtualNetworkSubnetResourceId": "[if(parameters('networkIsolation'), createObject('value', reference('network').outputs.defaultSubnetResourceId.value), createObject('value', ''))]", - "logAnalyticsWorkspaceResourceId": { - "value": "[reference('logAnalyticsWorkspace').outputs.resourceId.value]" - }, + "logAnalyticsWorkspaceResourceId": "[if(variables('useExistingLogAnalytics'), createObject('value', extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', variables('existingLawSubscription'), variables('existingLawResourceGroup')), 'Microsoft.OperationalInsights/workspaces', variables('existingLawName'))), createObject('value', reference('logAnalyticsWorkspace').outputs.resourceId.value))]", "roleAssignments": { "value": "[union(if(empty(parameters('userObjectId')), createArray(), createArray(createObject('principalId', parameters('userObjectId'), 'principalType', 'User', 'roleDefinitionIdOrName', 'Search Index Data Contributor'), createObject('principalId', parameters('userObjectId'), 'principalType', 'User', 'roleDefinitionIdOrName', 'Search Index Data Reader'))), createArray(createObject('principalId', reference('cognitiveServices').outputs.aiServicesSystemAssignedMIPrincipalId.value, 'principalType', 'ServicePrincipal', 'roleDefinitionIdOrName', 'Search Index Data Contributor'), createObject('principalId', reference('cognitiveServices').outputs.aiServicesSystemAssignedMIPrincipalId.value, 'principalType', 'ServicePrincipal', 'roleDefinitionIdOrName', 'Search Service Contributor')))]" }, @@ -61205,8 +61212,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "12098504235093504002" + "version": "0.36.177.2456", + "templateHash": "10434133969219228099" } }, "definitions": { @@ -66610,15 +66617,14 @@ "userObjectId": { "value": "[parameters('userObjectId')]" }, - "workspaceId": { - "value": "[reference('logAnalyticsWorkspace').outputs.resourceId.value]" - }, + "workspaceId": "[if(variables('useExistingLogAnalytics'), createObject('value', extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', variables('existingLawSubscription'), variables('existingLawResourceGroup')), 'Microsoft.OperationalInsights/workspaces', variables('existingLawName'))), createObject('value', reference('logAnalyticsWorkspace').outputs.resourceId.value))]", "location": { "value": "[parameters('location')]" }, "tags": { "value": "[variables('allTags')]" - } + }, + "dcrLocation": "[if(variables('useExistingLogAnalytics'), createObject('value', reference('existingLogAnalyticsWorkspace', '2023-09-01', 'full').location), createObject('value', reference('logAnalyticsWorkspace').outputs.location.value))]" }, "template": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", @@ -66626,8 +66632,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "18206245333790613128" + "version": "0.36.177.2456", + "templateHash": "5677507426497127040" } }, "parameters": { @@ -66795,6 +66801,12 @@ "metadata": { "description": "Specifies the resource tags." } + }, + "dcrLocation": { + "type": "string", + "metadata": { + "description": "Specified the location of the Data Collection Rules (DCR) resources." + } } }, "variables": { @@ -66960,7 +66972,7 @@ "type": "Microsoft.Insights/dataCollectionRules", "apiVersion": "2022-06-01", "name": "DCR-Win-Event-Logs-to-LAW", - "location": "[parameters('location')]", + "location": "[parameters('dcrLocation')]", "kind": "Windows", "properties": { "dataFlows": [ @@ -67006,7 +67018,7 @@ "type": "Microsoft.Insights/dataCollectionRules", "apiVersion": "2022-06-01", "name": "DCR-Win-Perf-to-LAW", - "location": "[parameters('location')]", + "location": "[parameters('dcrLocation')]", "kind": "Windows", "properties": { "dataFlows": [ @@ -67153,6 +67165,7 @@ } }, "dependsOn": [ + "existingLogAnalyticsWorkspace", "logAnalyticsWorkspace", "network", "storageAccount" @@ -67187,9 +67200,7 @@ "networkIsolation": { "value": "[parameters('networkIsolation')]" }, - "logAnalyticsWorkspaceResourceId": { - "value": "[reference('logAnalyticsWorkspace').outputs.resourceId.value]" - }, + "logAnalyticsWorkspaceResourceId": "[if(variables('useExistingLogAnalytics'), createObject('value', extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', variables('existingLawSubscription'), variables('existingLawResourceGroup')), 'Microsoft.OperationalInsights/workspaces', variables('existingLawName'))), createObject('value', reference('logAnalyticsWorkspace').outputs.resourceId.value))]", "virtualNetworkResourceId": "[if(parameters('networkIsolation'), createObject('value', reference('network').outputs.resourceId.value), createObject('value', ''))]", "virtualNetworkSubnetResourceId": "[if(parameters('networkIsolation'), createObject('value', reference('network').outputs.defaultSubnetResourceId.value), createObject('value', ''))]", "tags": { @@ -67202,8 +67213,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "9207373860269569676" + "version": "0.36.177.2456", + "templateHash": "9141196852468184318" } }, "parameters": { @@ -75118,9 +75129,7 @@ }, "virtualNetworkResourceId": "[if(parameters('networkIsolation'), createObject('value', reference('network').outputs.resourceId.value), createObject('value', ''))]", "virtualNetworkSubnetResourceId": "[if(parameters('networkIsolation'), createObject('value', reference('network').outputs.defaultSubnetResourceId.value), createObject('value', ''))]", - "logAnalyticsWorkspaceResourceId": { - "value": "[reference('logAnalyticsWorkspace').outputs.resourceId.value]" - }, + "logAnalyticsWorkspaceResourceId": "[if(variables('useExistingLogAnalytics'), createObject('value', extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', variables('existingLawSubscription'), variables('existingLawResourceGroup')), 'Microsoft.OperationalInsights/workspaces', variables('existingLawName'))), createObject('value', reference('logAnalyticsWorkspace').outputs.resourceId.value))]", "databases": { "value": "[parameters('cosmosDatabases')]" }, @@ -75136,8 +75145,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "14492739760745359867" + "version": "0.36.177.2456", + "templateHash": "13080886153333865760" } }, "definitions": { @@ -82512,8 +82521,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "18362011243916437863" + "version": "0.36.177.2456", + "templateHash": "16567675882447865792" } }, "definitions": { @@ -92564,9 +92573,7 @@ "keyVaultName": { "value": "[reference('keyvault').outputs.name.value]" }, - "logAnalyticsWorkspaceResourceId": { - "value": "[reference('logAnalyticsWorkspace').outputs.resourceId.value]" - }, + "logAnalyticsWorkspaceResourceId": "[if(variables('useExistingLogAnalytics'), createObject('value', extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', variables('existingLawSubscription'), variables('existingLawResourceGroup')), 'Microsoft.OperationalInsights/workspaces', variables('existingLawName'))), createObject('value', reference('logAnalyticsWorkspace').outputs.resourceId.value))]", "skuName": { "value": "B3" }, @@ -92617,8 +92624,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "16909276659049541183" + "version": "0.36.177.2456", + "templateHash": "6477409922388288779" } }, "definitions": { @@ -99146,8 +99153,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "5085333471373527815" + "version": "0.36.177.2456", + "templateHash": "9543015962309021398" } }, "definitions": { @@ -99393,7 +99400,7 @@ }, "AZURE_LOG_ANALYTICS_WORKSPACE_NAME": { "type": "string", - "value": "[reference('logAnalyticsWorkspace').outputs.name.value]" + "value": "[if(variables('useExistingLogAnalytics'), variables('existingLawName'), reference('logAnalyticsWorkspace').outputs.name.value)]" }, "AZURE_STORAGE_ACCOUNT_NAME": { "type": "string", diff --git a/infra/main.parameters.json b/infra/main.parameters.json index 9229397..b6be51b 100644 --- a/infra/main.parameters.json +++ b/infra/main.parameters.json @@ -82,6 +82,9 @@ ] } ] + }, + "existingLogAnalyticsWorkspaceId": { + "value": "${AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID}" } } } \ No newline at end of file diff --git a/infra/modules/virtualMachine.bicep b/infra/modules/virtualMachine.bicep index eae078e..d3c8c8b 100644 --- a/infra/modules/virtualMachine.bicep +++ b/infra/modules/virtualMachine.bicep @@ -81,6 +81,9 @@ param workspaceId string @description('Specifies the resource tags.') param tags object +@description('Specified the location of the Data Collection Rules (DCR) resources.') +param dcrLocation string + var randomString = uniqueString(resourceGroup().id, vmName, vmAdminPasswordOrKey) var adminPassword = (length(vmAdminPasswordOrKey) < 8) ? '${vmAdminPasswordOrKey}${take(randomString, 12)}' : vmAdminPasswordOrKey @@ -234,7 +237,7 @@ resource entraExtension 'Microsoft.Compute/virtualMachines/extensions@2023-09-01 resource dcrEventLogs 'Microsoft.Insights/dataCollectionRules@2022-06-01' = { name: 'DCR-Win-Event-Logs-to-LAW' - location: location + location: dcrLocation kind: 'Windows' properties: { dataFlows: [ @@ -279,7 +282,7 @@ resource dcrEventLogs 'Microsoft.Insights/dataCollectionRules@2022-06-01' = { resource dcrPerfLaw 'Microsoft.Insights/dataCollectionRules@2022-06-01' = { name: 'DCR-Win-Perf-to-LAW' - location: location + location: dcrLocation kind: 'Windows' properties: { dataFlows: [ From 2d50fab4ef98b99497f966935f74a8867bfb8a09 Mon Sep 17 00:00:00 2001 From: Prajwal-Microsoft Date: Mon, 21 Jul 2025 15:30:52 +0530 Subject: [PATCH 43/44] fix: Storage account name issue with special characters. (#68) --- infra/main.bicep | 3 ++- infra/main.json | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/infra/main.bicep b/infra/main.bicep index 86b5415..36c32a9 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -128,6 +128,7 @@ var defaultTags = { var allTags = union(defaultTags, tags) var resourceToken = substring(uniqueString(subscription().id, location, name), 0, 5) +var sanitizedName = toLower(replace(replace(replace(replace(replace(replace(replace(replace(replace(name, '@', ''), '#', ''), '$', ''), '!', ''), '-', ''), '_', ''), '.', ''), ' ', ''), '&', '')) var servicesUsername = take(replace(vmAdminUsername,'.', ''), 20) var deploySampleApp = appSampleEnabled && cosmosDbEnabled && searchEnabled && !empty(authClientId) && !empty(authClientSecret) && !empty(cosmosDatabases) && !empty(aiGPTModelDeployment) && length(aiEmbeddingModelDeployment) >= 2 @@ -229,7 +230,7 @@ module containerRegistry 'modules/containerRegistry.bicep' = if (acrEnabled) { module storageAccount 'modules/storageAccount.bicep' = { name: take('${name}-storage-account-deployment', 64) params: { - storageName: 'st${name}${resourceToken}' + storageName: 'st${sanitizedName}${resourceToken}' location: location networkIsolation: networkIsolation virtualNetworkResourceId: networkIsolation ? network.outputs.resourceId : '' diff --git a/infra/main.json b/infra/main.json index 86799e2..c4271b9 100644 --- a/infra/main.json +++ b/infra/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.36.177.2456", - "templateHash": "4954886504367674411" + "templateHash": "15134505383788492750" } }, "definitions": { @@ -1043,6 +1043,7 @@ }, "allTags": "[union(variables('defaultTags'), parameters('tags'))]", "resourceToken": "[substring(uniqueString(subscription().id, parameters('location'), parameters('name')), 0, 5)]", + "sanitizedName": "[toLower(replace(replace(replace(replace(replace(replace(replace(replace(replace(parameters('name'), '@', ''), '#', ''), '$', ''), '!', ''), '-', ''), '_', ''), '.', ''), ' ', ''), '&', ''))]", "servicesUsername": "[take(replace(parameters('vmAdminUsername'), '.', ''), 20)]", "deploySampleApp": "[and(and(and(and(and(and(and(parameters('appSampleEnabled'), parameters('cosmosDbEnabled')), parameters('searchEnabled')), not(empty(parameters('authClientId')))), not(empty(parameters('authClientSecret')))), not(empty(parameters('cosmosDatabases')))), not(empty(parameters('aiGPTModelDeployment')))), greaterOrEquals(length(parameters('aiEmbeddingModelDeployment')), 2))]", "authClientSecretName": "auth-client-secret" @@ -23340,7 +23341,7 @@ "mode": "Incremental", "parameters": { "storageName": { - "value": "[format('st{0}{1}', parameters('name'), variables('resourceToken'))]" + "value": "[format('st{0}{1}', variables('sanitizedName'), variables('resourceToken'))]" }, "location": { "value": "[parameters('location')]" From de0de33ceb41ed482f8cac1c30c6b11f9c496f32 Mon Sep 17 00:00:00 2001 From: Prajwal D C Date: Thu, 24 Jul 2025 12:03:42 +0530 Subject: [PATCH 44/44] fix: Shell script causing deployment to fail due to incorrect exit code --- scripts/auth_init.sh | 2 +- scripts/postprovision.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/auth_init.sh b/scripts/auth_init.sh index b6da77b..2dc23fa 100755 --- a/scripts/auth_init.sh +++ b/scripts/auth_init.sh @@ -4,7 +4,7 @@ if [ "$AZURE_APP_SAMPLE_ENABLED" = "false" ]; then echo "AZURE_APP_SAMPLE_ENABLED is false. Exiting auth_init script." - exit 1 + exit 0 fi echo 'Running "auth_init.py"' diff --git a/scripts/postprovision.sh b/scripts/postprovision.sh index 42e30cb..f402faa 100755 --- a/scripts/postprovision.sh +++ b/scripts/postprovision.sh @@ -4,7 +4,7 @@ if [ "$AZURE_APP_SAMPLE_ENABLED" = "false" ]; then echo "AZURE_APP_SAMPLE_ENABLED is false. Exiting auth_update script." - exit 1 + exit 0 fi echo 'Running "auth_update.py"'

256J^o&_!S9 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 literal 62039 zcma&NbyQnV)Hd1{cbB3KE(MAehZc8=I~3R8uEmOz;ts{#-3vvE7bqGCZpDKJ0^IcX zed~SiUw7TCm7L6($vHFo?Ah|{C()l&BFJ9pO{P%u2;#_X|;>E?W zg7il%ACu!p8yBj>tPCvs=VK7%gVIXW8fBNjsdn|L{qCe~@r*4W#iWose8M$Q)&C4< z*^;s`yY{dL<->sgSWRv&fE(P#kWbl7&}h_~0Y`MSk<-<_(h1BS=CN4CN*3ojecoclY%V zTKL(*Au)S?`i<5?5HSfMsyva)TTpygHQIh|>q$x*ozLS#WMCwGX@I&J9`&=8Lt@+v zXR7uG6zI$Q7Mzy9u;hmWZbH8_PNUTqTokx$^=yFW`j!Fd*2N~kjdr{eN}J~)!#mJe z#T+`9!dFZTW1G-T7hhV3CB)r0bFw#~=3pa8UjB8x(>REaN@Qn*JPimt#GA$)4{>Z= z&z!tD8&h-6@cISfaQrw95{v^Q*8O$E5$ElC8e-vR-)NPV9y4+&$7~ekBS%lM9NUm- z+B$!HzAkKQM*^Q_d9N@**QDAwBw_&&HTCE&MiWYrV_K5`KWZZzB|L3)~%{kZcXTq{jvb8^ZMQ++B1SIO~JlVg3>L=g?q4m3o}=cb#a|LB%hwu`b095#2< ze|Y)%*5{qbY08WU%BeNV>CNAzB5VMwAuKam1-6nGQ4D9*S#3qA5J09Mp+f-He%L7J zgrxQ4NW{|rGwr{TI>>brlmKKFL5Vc{B;p)eDRc#lscAQK{pGRgm~fQHZ+R#BT77Yb zB=vF>PK=Z}-}4>ms=j|q<7cU}``%EJ%b!fy0vTA%ikQ>oQrA=;-D$LHB4kE(sETO~ zgmp>DtGEPZNp?GvmStPjWVe1q+pX~$%R1q^63kHVs23cvxn6iG)W16=5mbNd%xnuk zn!?N;-~QRQIf5oAwyp!jGU;u@f7P6tKk6gP?&Ot@*98t9@d6n1!y3ZBRc*jCB;}`d zg3`8IV+>t-9ne3n*og_AH3Q4w2U1bmE>9mNf9bdcAp@js*d>lGSlFEkp_aGb!zaV& zwE7=b9ms(uy8*X;xXEiEMWD5cbJublYpe6LNJM!`v_p%pz|WszO5gDXF9QTzxfeUW z8U2i|YS1Dp(O3|g{PlrMAEzk5^Xd&Z0f97gYQ3X}c|8O&NMZD`t8Xdk#|Q1Vg6sM+ z54G?E$*9cH(^CIGqY;AabteMQChu$b4p1z3)nP}zJYq#PvazoGcXR+PNjpa-xb+me zgxcJhc@8ez5j98dr7rUEuI632T%F7;TMSwW-~HK$333Y)ee;z70opzcq17ZF1n